summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/rtems-bsps-aarch64.ini35
-rw-r--r--config/rtems-bsps-arm.ini24
-rw-r--r--config/rtems-bsps-microblaze.ini25
-rw-r--r--config/rtems-bsps-powerpc.ini1
-rw-r--r--config/rtems-bsps-riscv.ini16
-rw-r--r--config/rtems-bsps-tiers.ini36
-rw-r--r--config/rtems-bsps-x86_64.ini25
-rw-r--r--config/rtems-bsps.ini31
-rw-r--r--linkers/rtems-exeinfo.cpp48
-rw-r--r--linkers/rtems-score-thread.ini20
-rw-r--r--linkers/rtems-score-threadq.ini2
-rw-r--r--linkers/rtems-syms.cpp206
-rw-r--r--linkers/wscript2
-rw-r--r--misc/bin2c/rtems-bin2c.c42
-rw-r--r--misc/tools/config/rtems-boot.ini2
-rw-r--r--misc/wscript2
-rw-r--r--rtemstoolkit/SimpleIni.h598
-rw-r--r--rtemstoolkit/config.py2
-rw-r--r--rtemstoolkit/configuration.py2
-rw-r--r--rtemstoolkit/elftoolchain/libelf/_libelf_config.h40
-rwxr-xr-xrtemstoolkit/execute.py123
-rw-r--r--rtemstoolkit/libiberty/argv.c568
-rw-r--r--rtemstoolkit/libiberty/cp-demangle.c1384
-rw-r--r--rtemstoolkit/libiberty/cp-demangle.h11
-rw-r--r--rtemstoolkit/libiberty/cplus-dem.c4604
-rw-r--r--rtemstoolkit/libiberty/d-demangle.c954
-rw-r--r--rtemstoolkit/libiberty/demangle.h107
-rw-r--r--rtemstoolkit/libiberty/make-temp-file.c48
-rw-r--r--rtemstoolkit/libiberty/mkstemps.c2
-rw-r--r--rtemstoolkit/libiberty/pex-common.c2
-rw-r--r--rtemstoolkit/libiberty/pex-common.h2
-rw-r--r--rtemstoolkit/libiberty/pex-one.c2
-rw-r--r--rtemstoolkit/libiberty/pex-unix.c444
-rw-r--r--rtemstoolkit/libiberty/pex-win32.c97
-rw-r--r--rtemstoolkit/libiberty/rust-demangle.c1760
-rw-r--r--rtemstoolkit/libiberty/safe-ctype.c2
-rw-r--r--rtemstoolkit/libiberty/stpcpy.c4
-rw-r--r--rtemstoolkit/libiberty/xexit.c52
-rw-r--r--rtemstoolkit/libiberty/xmalloc.c25
-rw-r--r--rtemstoolkit/libiberty/xmemdup.c8
-rw-r--r--rtemstoolkit/macros.py58
-rw-r--r--rtemstoolkit/rld-dwarf.cpp2
-rw-r--r--rtemstoolkit/rld-elf.cpp14
-rw-r--r--rtemstoolkit/rld-elf.h5
-rw-r--r--rtemstoolkit/rld-path.cpp20
-rw-r--r--rtemstoolkit/rld-rap.cpp9
-rw-r--r--rtemstoolkit/rld-symbols.cpp3
-rw-r--r--rtemstoolkit/rld.cpp14
-rw-r--r--rtemstoolkit/version.py2
-rw-r--r--rtemstoolkit/wscript15
-rw-r--r--tester/covoar/ConfigFile.cc138
-rw-r--r--tester/covoar/ConfigFile.h8
-rw-r--r--tester/covoar/ExecutableInfo.cc103
-rw-r--r--tester/covoar/ExecutableInfo.h16
-rw-r--r--tester/covoar/Explanations.cc51
-rw-r--r--tester/covoar/Explanations.h14
-rw-r--r--tester/covoar/ObjdumpProcessor.cc284
-rw-r--r--tester/covoar/ObjdumpProcessor.h26
-rw-r--r--tester/covoar/ReportsBase.cc79
-rw-r--r--tester/covoar/SymbolTable.cc70
-rw-r--r--tester/covoar/TargetBase.cc35
-rw-r--r--tester/covoar/TargetBase.h18
-rw-r--r--tester/covoar/TargetFactory.cc24
-rw-r--r--tester/covoar/Target_aarch64.cc18
-rw-r--r--tester/covoar/Target_aarch64.h6
-rw-r--r--tester/covoar/Target_arm.cc16
-rw-r--r--tester/covoar/Target_arm.h6
-rw-r--r--tester/covoar/Target_i386.cc22
-rw-r--r--tester/covoar/Target_i386.h4
-rw-r--r--tester/covoar/Target_lm32.cc6
-rw-r--r--tester/covoar/Target_lm32.h4
-rw-r--r--tester/covoar/Target_m68k.cc12
-rw-r--r--tester/covoar/Target_m68k.h6
-rw-r--r--tester/covoar/Target_powerpc.cc8
-rw-r--r--tester/covoar/Target_powerpc.h4
-rw-r--r--tester/covoar/Target_riscv.cc6
-rw-r--r--tester/covoar/Target_riscv.h6
-rw-r--r--tester/covoar/Target_sparc.cc12
-rw-r--r--tester/covoar/Target_sparc.h6
-rw-r--r--tester/covoar/TraceConverter.cc119
-rw-r--r--tester/covoar/TraceList.cc14
-rw-r--r--tester/rt/config.py46
-rw-r--r--tester/rt/exe.py6
-rw-r--r--tester/rt/report.py12
-rw-r--r--tester/rt/stty.py2
-rw-r--r--tester/rt/test.py197
-rw-r--r--tester/rt/tftp.py44
-rw-r--r--tester/rt/tftpserver.py57
-rw-r--r--tester/rtems/testing/bsps/erc32-sis.ini5
-rw-r--r--tester/rtems/testing/bsps/gr740-sis.ini5
-rw-r--r--tester/rtems/testing/bsps/griscv-sis-cov.ini5
-rw-r--r--tester/rtems/testing/bsps/griscv-sis.ini5
-rw-r--r--tester/rtems/testing/bsps/imx7.ini2
-rw-r--r--tester/rtems/testing/bsps/leon2-sis.ini5
-rw-r--r--tester/rtems/testing/bsps/leon3-run.ini3
-rw-r--r--tester/rtems/testing/bsps/leon3-sis-cov.ini5
-rw-r--r--tester/rtems/testing/bsps/leon3-sis.ini5
-rw-r--r--tester/rtems/testing/bsps/qoriq_e500.ini2
-rw-r--r--tester/rtems/testing/bsps/qoriq_e6500_32.ini2
-rw-r--r--tester/rtems/testing/bsps/qoriq_e6500_64.ini2
-rw-r--r--tester/rtems/testing/bsps/rv32i.ini (renamed from tester/rtems/testing/bsps/rv64imac_medany_spike.ini)17
-rw-r--r--tester/rtems/testing/bsps/rv32imafdc.ini (renamed from tester/rtems/testing/bsps/rv64imafd_medany_spike.ini)17
-rw-r--r--tester/rtems/testing/bsps/rv64imac_spike.ini2
-rw-r--r--tester/rtems/testing/bsps/rv64imafd.ini (renamed from tester/rtems/testing/bsps/rv64imafd_medany.ini)7
-rw-r--r--tester/rtems/testing/bsps/rv64imafd_spike.ini2
-rw-r--r--tester/rtems/testing/bsps/rv64imafdc.ini (renamed from tester/rtems/testing/bsps/rv64imafdc_medany.ini)7
-rw-r--r--tester/rtems/testing/bsps/rv64imafdc_spike.ini2
-rw-r--r--tester/rtems/testing/bsps/sis-run.ini5
-rw-r--r--tester/rtems/testing/bsps/sis.ini6
-rw-r--r--tester/rtems/testing/bsps/stm32h7-stlink.ini (renamed from tester/rtems/testing/bsps/rv64imafdc_medany_spike.ini)22
-rw-r--r--tester/rtems/testing/qemu.cfg5
-rw-r--r--tester/rtems/testing/sis.cfg72
-rw-r--r--trace/record/record-main-lttng.cc21
-rw-r--r--trace/wscript20
-rwxr-xr-xwaf16
115 files changed, 6219 insertions, 7053 deletions
diff --git a/config/rtems-bsps-aarch64.ini b/config/rtems-bsps-aarch64.ini
new file mode 100644
index 0000000..a6199d7
--- /dev/null
+++ b/config/rtems-bsps-aarch64.ini
@@ -0,0 +1,35 @@
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2021 Joel Sherrill (joel@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-bsp-builder'.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#
+# AArch64 Architecture
+#
+[aarch64]
+bsps = a53_ilp32_qemu, a53_lp64_qemu,
+ a72_ilp32_qemu, a72_lp64_qemu,
+ xilinx_versal_aiedge,
+ xilinx_versal_qemu,
+ xilinx_versal_vck190,
+ xilinx_zynqmp_ilp32_qemu,
+ xilinx_zynqmp_ilp32_zu3eg,
+ xilinx_zynqmp_lp64_qemu,
+ xilinx_zynqmp_lp64_zu3eg
+
+exclude-smp = a53_ilp32_qemu, a53_lp64_qemu,
+ a72_ilp32_qemu, a72_lp64_qemu
diff --git a/config/rtems-bsps-arm.ini b/config/rtems-bsps-arm.ini
index 02b54e7..571fae9 100644
--- a/config/rtems-bsps-arm.ini
+++ b/config/rtems-bsps-arm.ini
@@ -26,9 +26,11 @@ bsps = altcycv_devkit,
beagleboardorig, beagleboardxm, beagleboneblack, beaglebonewhite,
csb336, csb337, csb637,
edb7312,
+ fvp_cortex_r52,
kit637_v6,
gumstix,
imx7,
+ imxrt1052,
lm3s3749, lm3s6965, lm3s6965_qemu, lm4f120,
lpc1768_mbed, lpc1768_mbed_ahb_ram, lpc1768_mbed_ahb_ram_eth,
lpc17xx_ea_ram, lpc17xx_ea_rom_int, lpc17xx_plx800_ram,
@@ -37,22 +39,29 @@ bsps = altcycv_devkit,
lpc24xx_plx800_ram, lpc24xx_plx800_rom_int, lpc40xx_ea_ram,
lpc40xx_ea_rom_int, lpc32xx_mzx, lpc32xx_mzx_stage_1,
lpc32xx_mzx_stage_2, lpc32xx_phycore,
+ nucleo-h743zi,
raspberrypi, raspberrypi2,
realview_pbx_a9_qemu,
rtl22xx, rtl22xx_t,
smdk2410,
- stm32f105rc, stm32f4,
+ stm32f105rc, stm32f4, stm32h7, stm32h7b3i-dk, stm32h757i-eval,
+ stm32h757i-eval-m4, stm32h747i-disco, stm32h747i-disco-m4,
tms570ls3137_hdk, tms570ls3137_hdk_intram,
tms570ls3137_hdk_sdram,
- tms570ls3137_hdk_with_loader,
+ tms570lc4357_hdk,
+ tms570lc4357_hdk_sdram,
+ xen_virtual,
xilinx_zynq_zc702, xilinx_zynq_zc706, xilinx_zynq_zedboard,
- xilinx_zynq_a9_qemu
+ xilinx_zynq_a9_qemu,
+ xilinx_zynqmp_ultra96
exclude-smp = atsamv,
beagleboardorig, beagleboardxm, beagleboneblack, beaglebonewhite,
csb336, csb337, csb637,
edb7312,
+ fvp_cortex_r52,
kit637_v6,
gumstix,
+ imxrt1052,
lm3s3749, lm3s6965, lm3s6965_qemu, lm4f120,
lpc1768_mbed, lpc1768_mbed_ahb_ram, lpc1768_mbed_ahb_ram_eth,
lpc17xx_ea_ram, lpc17xx_ea_rom_int, lpc17xx_plx800_ram,
@@ -61,11 +70,14 @@ exclude-smp = atsamv,
lpc24xx_plx800_ram, lpc24xx_plx800_rom_int, lpc40xx_ea_ram,
lpc40xx_ea_rom_int, lpc32xx_mzx, lpc32xx_mzx_stage_1,
lpc32xx_mzx_stage_2, lpc32xx_phycore,
+ nucleo-h743zi,
raspberrypi, raspberrypi2,
rtl22xx, rtl22xx_t,
smdk2410,
- stm32f105rc, stm32f4,
+ stm32f105rc, stm32f4, stm32h7,
+ stm32h7b3i-dk, stm32h757i-eval, stm32h757i-eval-m4,
+ stm32h747i-disco, stm32h747i-disco-m4,
tms570ls3137_hdk, tms570ls3137_hdk_intram,
tms570ls3137_hdk_sdram,
- tms570ls3137_hdk_with_loader
-exclude-network = altcycv_devkit, realview_pbx_a9_qemu
+ xen_virtual,
+ xilinx_zynqmp_ultra96
diff --git a/config/rtems-bsps-microblaze.ini b/config/rtems-bsps-microblaze.ini
new file mode 100644
index 0000000..b886dbd
--- /dev/null
+++ b/config/rtems-bsps-microblaze.ini
@@ -0,0 +1,25 @@
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2022 Joel Sherrill (joel@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-bsp-builder'.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#
+# Microblaze Architecture
+#
+[microblaze]
+bsps = kcu105, kcu105_qemu
+exclude = smp
diff --git a/config/rtems-bsps-powerpc.ini b/config/rtems-bsps-powerpc.ini
index b4dc02c..fc69827 100644
--- a/config/rtems-bsps-powerpc.ini
+++ b/config/rtems-bsps-powerpc.ini
@@ -24,7 +24,6 @@
bsps = beatnik,
br_uid,
gwlcfm,
- haleakala,
hsc_cm01,
icecube,
mcp750,
diff --git a/config/rtems-bsps-riscv.ini b/config/rtems-bsps-riscv.ini
index da3a5a4..816675f 100644
--- a/config/rtems-bsps-riscv.ini
+++ b/config/rtems-bsps-riscv.ini
@@ -1,6 +1,7 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
# Copyright 2018 embedded brains GmbH
+# Copyright 2022 OAR Corporation
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-bsp-builder'.
@@ -21,7 +22,14 @@
# RISC-V Architecture
#
[riscv]
-bsps = rv32iac, rv32i, rv32imac, rv32imafc, rv32imafdc, rv32imafd, rv32im,
- rv64imac, rv64imac_medany, rv64imafdc, rv64imafd, rv64imafdc_medany,
- rv64imafd_medany
-exclude-smp = rv32i, rv32im
+bsps = frdme310arty,
+ griscv, grv32i, grv32im, grv32imac, grv32imafdc,
+ mpfs64imafdc,
+ noel32im, noel32imafd, noel64imac, noel64imafd, noel64imafdc,
+ rv32i, rv32iac, rv32im, rv32imac, rv32imafc, rv32imafd, rv32imafdc,
+ rv64imac, rv64imafd, rv64imafdc
+
+exclude-smp = frdme310arty,
+ grv32i, grv32im,
+ noel32im,
+ rv32i, rv32im
diff --git a/config/rtems-bsps-tiers.ini b/config/rtems-bsps-tiers.ini
index 875c5df..3406e68 100644
--- a/config/rtems-bsps-tiers.ini
+++ b/config/rtems-bsps-tiers.ini
@@ -21,17 +21,21 @@
# Tier 1: no build errors and no unexpected tests failures on hardware.
#
[tier-1]
-archs = arm, i386, powerpc
+archs = aarch64, arm, i386, powerpc
+bsps_aarch64 = xilinx_zynqmp_lp64_zu3eg, xilinx_zynqmp_ilp32_zu3eg
bsps_arm = beagleboneblack, imx7, xilinx_zynq_zedboard
bsps_i386 = pc686
bsps_powerpc = qoriq_e500
#
-# Tier 2: no build errors and no unexpected tests failures on hardware and
-# simulators.
+# Tier 2: no build errors and no unexpected tests failures on simulators.
#
[tier-2]
-archs = mips, powerpc, sparc
+archs = aarch64, mips, powerpc, sparc
+bsps_aarch64 = a53_ilp32_qemu, a53_lp64_qemu,
+ a72_ilp32_qemu, a72_lp64_qemu,
+ xilinx_versal_lp64_qemu,
+ xilinx_zynqmp_ilp32_qemu, xilinx_zynqmp_lp64_qemu
bsps_mips = jmr3904
bsps_powerpc = psim
bsps_sparc = erc32, leon2, leon3
@@ -40,15 +44,18 @@ bsps_sparc = erc32, leon2, leon3
# Tier 3: no build errors, no tests run.
#
[tier-3]
-archs = arm, bfin, i386, lm32, m68k, moxie,
- nios2, or1k, sh, sparc64, v850, x86_64
+archs = arm, aarch64, bfin, i386, lm32, m68k, microblaze, moxie,
+ nios2, or1k, riscv, sh, sparc64, v850, x86_64
+bsps_aarch64 = xilinx_versal_lp64_vck190
bsps_arm = altcycv_devkit,
- arm1136jfs, arm1136js, arm7tdmi, arm920, armcortexa9, atsamv,
+ atsamv,
beagleboardorig, beagleboardxm, beaglebonewhite,
csb336, csb337, csb637,
edb7312,
+ fvp_cortex_r52,
kit637_v6,
gumstix,
+ imxrt1052,
lm3s3749, lm3s6965, lm3s6965_qemu, lm4f120,
lpc1768_mbed, lpc1768_mbed_ahb_ram, lpc1768_mbed_ahb_ram_eth,
lpc17xx_ea_ram, lpc17xx_ea_rom_int, lpc17xx_plx800_ram,
@@ -57,18 +64,22 @@ bsps_arm = altcycv_devkit,
lpc24xx_plx800_ram, lpc24xx_plx800_rom_int, lpc40xx_ea_ram,
lpc40xx_ea_rom_int, lpc32xx_mzx, lpc32xx_mzx_stage_1,
lpc32xx_mzx_stage_2, lpc32xx_phycore,
+ nucleo-h743zi,
raspberrypi, raspberrypi2,
realview_pbx_a9_qemu,
rtl22xx, rtl22xx_t,
smdk2410,
- stm32f105rc, stm32f4,
+ stm32f105rc, stm32f4, stm32h7, stm32h7b3i-dk, stm32h757i-eval,
+ stm32h757i-eval-m4, stm32h747i-disco, stm32h747i-disco-m4,
tms570ls3137_hdk, tms570ls3137_hdk_intram,
tms570ls3137_hdk_sdram,
- tms570ls3137_hdk_with_loader,
- xilinx_zynq_a9_qemu, xilinx_zynq_zc702, xilinx_zynq_zc706
+ xen_virtual,
+ xilinx_zynq_a9_qemu, xilinx_zynq_zc702, xilinx_zynq_zc706,
+ xilinx_zynqmp_ultra96
bsps_bfin = TLL6527M, bf537Stamp, eZKit533
bsps_i386 = pc386, pc486, pc586-sse, pc586, pcp4
bsps_lm32 = lm32_evr, lm32_evr_gdbsim, milkymist
+bsps_microblaze = kcu105, kcu105_qemu
bsps_m68k = av5282,
csb360,
gen68340, gen68360, gen68360_040,
@@ -110,6 +121,11 @@ bsps_powerpc = beatnik,
t32mppc,
tqm8xx_stk8xx,
virtex, virtex4, virtex5
+bsps_riscv = griscv, grv32i, grv32im, grv32imac, grv32imafdc,
+ frdme310arty,
+ rv32i, rv32iac, rv32im, rv32imac, rv32imafc, rv32imafd, rv32imafdc,
+ rv64imac, rv64imac_medany, rv64imafd, rv64imafd_medany,
+ rv64imafdc, rv64imafdc_medany
bsps_sh = gensh1, gensh2, gensh4, simsh1, simsh2, simsh2e, simsh4
bsps_sparc = at697f, gr712rc, ut699, ut700, gr740
bsps_sparc64 = niagara, usiii
diff --git a/config/rtems-bsps-x86_64.ini b/config/rtems-bsps-x86_64.ini
new file mode 100644
index 0000000..eeccfea
--- /dev/null
+++ b/config/rtems-bsps-x86_64.ini
@@ -0,0 +1,25 @@
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2022 Joel Sherrill (joel@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-bsp-builder'.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#
+# x86_64 Architecture
+#
+[x86_64]
+bsps = amd64
+exclude = smp
diff --git a/config/rtems-bsps.ini b/config/rtems-bsps.ini
index d5de667..fdda5e7 100644
--- a/config/rtems-bsps.ini
+++ b/config/rtems-bsps.ini
@@ -60,27 +60,34 @@ include = rtems-bsps-tiers.ini
# All the architectures and BSPs.
#
[everything]
-archs = arm,
+archs = aarch64,
+ arm,
bfin,
i386,
lm32,
m68k,
+ microblaze,
mips,
moxie,
+ nios2,
or1k,
powerpc,
riscv,
sh,
sparc,
sparc64,
- v850
+ v850,
+ x86_64
+bsps_aarch64 = ${aarch64:bsps}
bsps_arm = ${arm:bsps}
bsps_bfin = ${bfin:bsps}
bsps_i386 = ${i386:bsps}
bsps_lm32 = ${lm32:bsps}
bsps_m68k = ${m68k:bsps}
+bsps_microblaze = ${microblaze:bsps}
bsps_mips = ${mips:bsps}
bsps_moxie = ${moxie:bsps}
+bsps_nios2 = ${nios2:bsps}
bsps_or1k = ${or1k:bsps}
bsps_powerpc = ${powerpc:bsps}
bsps_riscv = ${riscv:bsps}
@@ -88,25 +95,30 @@ bsps_sh = ${sh:bsps}
bsps_sparc = ${sparc:bsps}
bsps_sparc64 = ${sparc64:bsps}
bsps_v850 = ${v850:bsps}
+bsps_x86_64 = ${x86_64:bsps}
#
# Architectures
#
[architectures]
-include = rtems-bsps-arm.ini,
+include = rtems-bsps-aarch64.ini,
+ rtems-bsps-arm.ini,
rtems-bsps-bfin.ini,
rtems-bsps-i386.ini,
rtems-bsps-lm32.ini,
rtems-bsps-m68k.ini,
+ rtems-bsps-microblaze.ini,
rtems-bsps-mips.ini,
rtems-bsps-moxie.ini,
+ rtems-bsps-nios2.ini,
rtems-bsps-or1k.ini,
rtems-bsps-powerpc.ini,
rtems-bsps-riscv.ini,
rtems-bsps-sh.ini,
rtems-bsps-sparc.ini,
rtems-bsps-sparc64.ini,
- rtems-bsps-v850.ini
+ rtems-bsps-v850.ini,
+ rtems-bsps-x86_64.ini
#
# The Build Options define how each combination is to be build.
@@ -129,9 +141,7 @@ no-tests = config:base
# The all build.
#
all = debug, profiling, smp, smp-debug,
- posix, no-posix, posix-debug, posix-profiling,
- network, no-network, network-debug,
- smp-network, smp-network-debug
+ posix, no-posix, posix-debug, posix-profiling
#
# The options for each varations.
#
@@ -143,11 +153,6 @@ posix = config:base, config:posix
no-posix = config:base, config:no-posix
posix-debug = config:base, config:posix, config:debug
posix-profiling = config:base, config:posix, config:profiling
-network = config:base, config:network
-no-network = config:base, config:no-network
-network-debug = config:base, config:network, config:debug
-smp-network = config:base, config:smp, config:network
-smp-network-debug = config:base, config:smp, config:network, config:debug
#
# The config section holds the configuration options used in the builds.
@@ -174,5 +179,3 @@ smp = RTEMS_SMP=True
no-smp = RTEMS_SMP=False
posix = RTEMS_POSIX_API=True
no-posix = RTEMS_POSIX_API=False
-network = RTEMS_NETWORKING=True
-no-network = RTEMS_NETWORKING=False
diff --git a/linkers/rtems-exeinfo.cpp b/linkers/rtems-exeinfo.cpp
index e54f9b2..0b43403 100644
--- a/linkers/rtems-exeinfo.cpp
+++ b/linkers/rtems-exeinfo.cpp
@@ -144,7 +144,7 @@ namespace rld
/**
* Load the executable file.
*/
- image (const std::string exe_name);
+ image (const std::string exe_name, bool load_functions);
/**
* Clean up.
@@ -223,8 +223,7 @@ namespace rld
* Helper for for_each to filter and load the sections we wish to
* dump.
*/
- class section_loader:
- public std::unary_function < const files::section, void >
+ class section_loader
{
public:
@@ -283,7 +282,7 @@ namespace rld
}
}
- image::image (const std::string exe_name)
+ image::image (const std::string exe_name, bool load_functions)
: exe (exe_name),
init (0),
fini (0)
@@ -321,7 +320,11 @@ namespace rld
debug.load_debug ();
debug.load_types ();
debug.load_variables ();
- debug.load_functions ();
+ if (load_functions)
+ {
+ std::cout << "May take a while ..." << std::endl;
+ debug.load_functions ();
+ }
symbols.globals (addresses);
symbols.weaks (addresses);
symbols.locals (addresses);
@@ -610,23 +613,26 @@ namespace rld
uint32_t address;
symbols::symbol* sym;
sec.data >> address;
- sym = addresses[address];
- std::cout << " "
- << std::hex << std::setfill ('0')
- << "0x" << std::setw (8) << address
- << std::dec << std::setfill ('0');
- if (sym)
- {
- std::string label = sym->name ();
- if (rld::symbols::is_cplusplus (label))
- rld::symbols::demangle_name (label, label);
- std::cout << " " << label;
- }
- else
+ if (address != 0)
{
- std::cout << " no symbol";
+ sym = addresses[address];
+ std::cout << " "
+ << std::hex << std::setfill ('0')
+ << "0x" << std::setw (8) << address
+ << std::dec << std::setfill ('0');
+ if (sym)
+ {
+ std::string label = sym->name ();
+ if (rld::symbols::is_cplusplus (label))
+ rld::symbols::demangle_name (label, label);
+ std::cout << " " << label;
+ }
+ else
+ {
+ std::cout << " no symbol (maybe static to a module)";
+ }
+ std::cout << std::endl;
}
- std::cout << std::endl;
}
}
@@ -1114,7 +1120,7 @@ main (int argc, char* argv[])
/*
* Open the executable and read the symbols.
*/
- rld::exeinfo::image exe (exe_name);
+ rld::exeinfo::image exe (exe_name, inlined | dwarf_data);
std::cout << "exe: " << exe.exe.name ().full () << std::endl
<< std::endl;
diff --git a/linkers/rtems-score-thread.ini b/linkers/rtems-score-thread.ini
index cc00d2b..a759f72 100644
--- a/linkers/rtems-score-thread.ini
+++ b/linkers/rtems-score-thread.ini
@@ -3,9 +3,9 @@
;
[rtems-score-thread-all]
trace = _Thread_Handler_initialization, _Thread_Create_idle, _Thread_Start_multitasking
-trace = _Stack_Allocate, _Stack_Free, _Thread_Initialize, _Thread_Start
-trace = _Thread_Restart_other, _Thread_Restart_self, _Thread_Yield, _Thread_Set_life_protection
-trace = _Thread_Kill_zombies, _Thread_Close
+trace = _Stack_Allocate, _Stack_Free, _Thread_Start
+trace = _Thread_Yield, _Thread_Set_life_protection
+trace = _Thread_Kill_zombies
trace = _Thread_Clear_state, _Thread_Set_state, _Thread_Load_environment
trace = _Thread_Handler
trace = _Thread_Get
@@ -15,14 +15,14 @@ traces = rtems-score-thread-create, rtems-score-thread-destroy
[rtems-score-thread-create]
trace = _Thread_Handler_initialization, _Thread_Create_idle
-trace = _Stack_Allocate, _Thread_Initialize, _Thread_Start
-trace = _Thread_Restart_other, _Thread_Restart_self, _Thread_Handler
+trace = _Stack_Allocate, _Thread_Start
+trace = _Thread_Restart, _Thread_Handler
[rtems-score-thread-destroy]
-trace = _Thread_Kill_zombies, _Thread_Close
+trace = _Thread_Kill_zombies
[rtems-score-thread-activity]
-trace = _Thread_Restart_other, _Thread_Restart_self, _Thread_Yield, _Thread_Set_life_protection
+trace = _Thread_Yield, _Thread_Set_life_protection
trace = _Thread_Clear_state,
trace = _Thread_Set_state, _Thread_Load_environment
trace = _Thread_Get
@@ -34,14 +34,10 @@ _Thread_Create_idle = void, void
_Thread_Start_multitasking = void, void
_Stack_Allocate = void*, size_t
_Stack_Free = void, void*
-_Thread_Initialize = Status_Control, Thread_Information*, Thread_Control*, const Thread_Configuration*
-_Thread_Start = bool, Thread_Control*, const Thread_Entry_information*, ISR_lock_Context*
-_Thread_Restart_other = bool, Thread_Control*, const Thread_Entry_information*, ISR_lock_Context*
-_Thread_Restart_self = void, Thread_Control*, const Thread_Entry_information*, ISR_lock_Context*
+_Thread_Start = Status_Control, Thread_Control*, const Thread_Entry_information*, ISR_lock_Context*
_Thread_Yield = void, Thread_Control*
_Thread_Set_life_protection = Thread_Life_state, Thread_Life_state
_Thread_Kill_zombies = void, void
-_Thread_Close = void, Thread_Control*, Thread_Control*, Thread_Close_context*
_Thread_Clear_state = States_Control, Thread_Control*, States_Control
_Thread_Set_state = States_Control, Thread_Control*, States_Control
_Thread_Load_environment = void, Thread_Control*
diff --git a/linkers/rtems-score-threadq.ini b/linkers/rtems-score-threadq.ini
index 23b09bf..3763990 100644
--- a/linkers/rtems-score-threadq.ini
+++ b/linkers/rtems-score-threadq.ini
@@ -4,9 +4,7 @@
[rtems-score-threadq-all]
trace = _Thread_queue_Enqueue
trace = _Thread_queue_Extract_locked
-trace = _Thread_queue_Unblock_critical
[rtems-score-threadq-signatures]
_Thread_queue_Enqueue = void, Thread_queue_Queue*, const Thread_queue_Operations*, Thread_Control*, Thread_queue_Context*
_Thread_queue_Extract_locked = bool, Thread_queue_Queue*, const Thread_queue_Operations*, Thread_Control*, Thread_queue_Context*
-_Thread_queue_Unblock_critical = void, bool, Thread_queue_Queue*, Thread_Control*, ISR_lock_Context*
diff --git a/linkers/rtems-syms.cpp b/linkers/rtems-syms.cpp
index bfe2a48..9dd845f 100644
--- a/linkers/rtems-syms.cpp
+++ b/linkers/rtems-syms.cpp
@@ -53,19 +53,31 @@
/**
* Header text.
*/
-static const char* c_header[] =
+static const char* const c_header[] =
{
"/*",
" * RTEMS Global Symbol Table",
" * Automatically generated. Do not edit..",
" */",
"",
+ "#include <stddef.h>",
+ "#include <stdint.h>",
+ "",
+ "extern void* rtems_rtl_tls_get_base (void);",
+ "",
"extern const unsigned char rtems__rtl_base_globals[];",
"extern const unsigned int rtems__rtl_base_globals_size[];",
"",
- "void rtems_rtl_base_sym_global_add (const unsigned char* , unsigned int );",
+ "typedef size_t (*rtems_rtl_tls_offset_func)(void);",
+ "typedef struct rtems_rtl_tls_offset {",
+ " size_t index;",
+ " rtems_rtl_tls_offset_func offset;",
+ "} rtems_rtl_tls_offset;",
"",
- "asm(\".section \\\".rodata\\\"\");",
+ "void rtems_rtl_base_sym_global_add (const unsigned char* , unsigned int,",
+ " rtems_rtl_tls_offset*, size_t );",
+ "",
+ "asm(\".pushsection \\\".rodata\\\"\");",
"",
"asm(\" .align 4\");",
"asm(\" .local rtems__rtl_base_globals\");",
@@ -78,35 +90,61 @@ static const char* c_header[] =
0
};
-static const char* c_trailer[] =
+static const char* const c_sym_table_end[] =
{
"asm(\" .byte 0\");",
"asm(\" .ascii \\\"\\xde\\xad\\xbe\\xef\\\"\");",
-#if BROKEN_ON_SOME_ASSEMBLERS
- "asm(\" .type rtems__rtl_base_globals, #object\");",
- "asm(\" .size rtems__rtl_base_globals, . - rtems__rtl_base_globals\");",
-#endif
"",
+ 0
+};
+
+static const char* const c_tls_call_table_start[] =
+{
+ "rtems_rtl_tls_offset rtems_rtl_tls_offsets[] = {",
+ 0
+};
+
+static const char* const c_tls_call_table_end[] =
+{
+ "};",
+ "#define RTEMS_RTL_TLS_OFFSETS_NUM " \
+ "(sizeof(rtems_rtl_tls_offsets) / (sizeof(rtems_rtl_tls_offsets[0])))",
+ "",
+ 0
+};
+
+static const char* const c_trailer[] =
+{
"/*",
" * Symbol table size.",
" */",
"asm(\" .align 4\");",
"asm(\" .local rtems__rtl_base_globals_size\");",
-#if BROKEN_ON_SOME_ASSEMBLERS
- "asm(\" .type rtems__rtl_base_globals_size, #object\");",
- "asm(\" .size rtems__rtl_base_globals_size, 4\");",
-#endif
"asm(\"rtems__rtl_base_globals_size:\");",
"asm(\" .long rtems__rtl_base_globals_size - rtems__rtl_base_globals\");",
+ "asm(\" .popsection\");",
"",
0
};
-static const char* c_rtl_call_body[] =
+static const char* const c_rtl_call_body_embeded[] =
{
"{",
" rtems_rtl_base_sym_global_add (&rtems__rtl_base_globals[0],",
- " rtems__rtl_base_globals_size[0]);",
+ " rtems__rtl_base_globals_size[0],",
+ " &rtems_rtl_tls_offsets[0],",
+ " RTEMS_RTL_TLS_OFFSETS_NUM);",
+ "}",
+ 0
+};
+
+static const char* const c_rtl_call_body[] =
+{
+ "{",
+ " rtems_rtl_base_sym_global_add (&rtems__rtl_base_globals[0],",
+ " rtems__rtl_base_globals_size[0],",
+ " NULL,",
+ " 0);",
"}",
0
};
@@ -115,7 +153,7 @@ static const char* c_rtl_call_body[] =
* Paint the data to the temporary file.
*/
static void
-temporary_file_paint (rld::process::tempfile& t, const char* lines[])
+temporary_file_paint (rld::process::tempfile& t, const char* const lines[])
{
for (int l = 0; lines[l]; ++l)
t.write_line (lines[l]);
@@ -140,7 +178,7 @@ c_embedded_trailer (rld::process::tempfile& c)
{
c.write_line ("void rtems_rtl_base_global_syms_init(void);");
c.write_line ("void rtems_rtl_base_global_syms_init(void)");
- temporary_file_paint (c, c_rtl_call_body);
+ temporary_file_paint (c, c_rtl_call_body_embeded);
}
/**
@@ -214,16 +252,27 @@ symbol_filter::filter (const rld::symbols::symtab& symbols,
struct output_sym
{
+ enum struct output_mode {
+ symbol,
+ tls_func,
+ tls_call_table
+ };
rld::process::tempfile& c;
const bool embed;
const bool weak;
-
- output_sym(rld::process::tempfile& c,
- bool embed,
- bool weak)
- : c (c),
- embed (embed),
- weak (weak) {
+ const output_mode mode;
+ size_t& index;
+
+ output_sym(rld::process::tempfile& c_,
+ bool embed_,
+ bool weak_,
+ output_mode mode_,
+ size_t& index_)
+ : c (c_),
+ embed (embed_),
+ weak (weak_),
+ mode (mode_),
+ index (index_) {
}
void operator ()(const rld::symbols::symtab::value_type& value);
@@ -240,34 +289,66 @@ output_sym::operator ()(const rld::symbols::symtab::value_type& value)
if (weak && sym.value () == 0)
return;
- c.write_line ("asm(\" .asciz \\\"" + sym.name () + "\\\"\");");
-
- if (embed)
- {
- c.write_line ("#if __riscv_xlen == 64");
- c.write_line ("asm(\" .quad " + sym.name () + "\");");
- c.write_line ("#else");
- c.write_line ("asm(\" .long " + sym.name () + "\");");
- c.write_line ("#endif");
- }
- else
- {
- std::stringstream oss;
- oss << std::hex << std::setfill ('0') << std::setw (8) << sym.value ();
- c.write_line ("#if __riscv_xlen == 64");
- c.write_line ("asm(\" .quad 0x" + oss.str () + "\");");
- c.write_line ("#else");
- c.write_line ("asm(\" .long 0x" + oss.str () + "\");");
- c.write_line ("#endif");
+ switch (mode) {
+ case output_mode::symbol:
+ default:
+ /*
+ * Set TLS value to 0. It is filled in at run time by the call
+ * table.
+ */
+ c.write_line ("asm(\" .asciz \\\"" + sym.name () + "\\\"\");");
+ {
+ std::string val;
+ if (sym.type () == STT_TLS) {
+ val = "0";
+ } else {
+ if (embed) {
+ val = sym.name ();
+ } else {
+ std::stringstream oss;
+ oss << std::hex << std::showbase << std::internal <<
+ std::setfill ('0') << std::setw (10) << sym.value ();
+ val = oss.str ();
+ }
+ }
+ c.write_line ("#if __SIZEOF_POINTER__ == 8");
+ c.write_line ("asm(\" .quad " + val + "\");");
+ c.write_line ("#else");
+ c.write_line ("asm(\" .long " + val + "\");");
+ c.write_line ("#endif");
+ }
+ break;
+ case output_mode::tls_func:
+ if (sym.type () == STT_TLS) {
+ c.write_line ("#define RTEMS_TLS_INDEX_" + sym.name () + " " + std::to_string(index));
+ c.write_line ("static size_t rtems_rtl_tls_" + sym.name () + "(void) {");
+ c.write_line (" extern __thread char " + sym.name () + "[];");
+ c.write_line (" size_t tls_base = (size_t) rtems_rtl_tls_get_base ();");
+ c.write_line (" size_t tls_addr = (size_t) " + sym.name () + ";");
+ c.write_line (" return tls_addr - tls_base;");
+ c.write_line ("}");
+ c.write_line ("");
+ }
+ break;
+ case output_mode::tls_call_table:
+ if (sym.type () == STT_TLS) {
+ c.write_line (" { RTEMS_TLS_INDEX_" + sym.name () +
+ ", rtems_rtl_tls_" + sym.name () + " },");
+ }
+ break;
}
+ ++index;
}
-
static void
generate_c (rld::process::tempfile& c,
rld::symbols::symtab& symbols,
bool embed)
{
+ if (rld::verbose ())
+ std::cout << "symbol C file: " << c.name () << std::endl;
+
+ c.open (true);
temporary_file_paint (c, c_header);
/*
@@ -276,9 +357,28 @@ generate_c (rld::process::tempfile& c,
* longer weak and should be consider a global symbol. You cannot link a
* global symbol with the same in a dynamically loaded module.
*/
+ size_t index = 0;
std::for_each (symbols.begin (),
symbols.end (),
- output_sym (c, embed, false));
+ output_sym (c, embed, false,
+ output_sym::output_mode::symbol, index));
+
+ temporary_file_paint (c, c_sym_table_end);
+
+ if (embed) {
+ index = 0;
+ std::for_each (symbols.begin (),
+ symbols.end (),
+ output_sym (c, embed, false,
+ output_sym::output_mode::tls_func, index));
+ temporary_file_paint (c, c_tls_call_table_start);
+ index = 0;
+ std::for_each (symbols.begin (),
+ symbols.end (),
+ output_sym (c, embed, false,
+ output_sym::output_mode::tls_call_table, index));
+ temporary_file_paint (c, c_tls_call_table_end);
+ }
temporary_file_paint (c, c_trailer);
@@ -294,11 +394,6 @@ generate_symmap (rld::process::tempfile& c,
rld::symbols::symtab& symbols,
bool embed)
{
- c.open (true);
-
- if (rld::verbose ())
- std::cout << "symbol C file: " << c.name () << std::endl;
-
generate_c (c, symbols, embed);
if (rld::verbose ())
@@ -526,8 +621,8 @@ main (int argc, char* argv[])
throw rld::error ("no kernel file", "options");
if (argc != 1)
throw rld::error ("only one kernel file", "options");
- if (output.empty () && map.empty ())
- throw rld::error ("no output or map", "options");
+ if (output.empty () && symc.empty() && map.empty ())
+ throw rld::error ("no output, symbol C file, or map", "options");
kernel_name = *argv;
@@ -587,7 +682,7 @@ main (int argc, char* argv[])
/*
* Create an output file if asked too.
*/
- if (!output.empty ())
+ if (!output.empty () || !symc.empty())
{
rld::process::tempfile c (".c");
@@ -598,9 +693,12 @@ main (int argc, char* argv[])
}
/*
- * Generate and compile the symbol map.
+ * Generate and if requested compile the symbol map.
*/
- generate_symmap (c, output, filter_symbols, embed);
+ if (output.empty())
+ generate_c (c, filter_symbols, embed);
+ else
+ generate_symmap (c, output, filter_symbols, embed);
}
kernel.close ();
diff --git a/linkers/wscript b/linkers/wscript
index 8591d60..2d2d7f0 100644
--- a/linkers/wscript
+++ b/linkers/wscript
@@ -178,4 +178,4 @@ def build(bld):
use = modules)
def tags(ctx):
- ctx.exec_command('etags $(find . -name \*.[sSch])', shell = True)
+ ctx.exec_command('etags $(find . -name \\*.[sSch])', shell = True)
diff --git a/misc/bin2c/rtems-bin2c.c b/misc/bin2c/rtems-bin2c.c
index 462ecf0..8d0e6a1 100644
--- a/misc/bin2c/rtems-bin2c.c
+++ b/misc/bin2c/rtems-bin2c.c
@@ -42,6 +42,7 @@ int verbose = 0;
int zeroterminated = 0;
int createC = 1;
int createH = 1;
+unsigned int align = 0;
static void sanitize_file_name(char *p)
{
@@ -175,11 +176,22 @@ void process(const char *ifname, const char *ofname, const char *forced_name)
/* print structure */
fprintf(
ocfile,
- "%s%sunsigned char %s[] = {\n ",
+ "%s%sunsigned char %s[] ",
((usestatic) ? "static " : ""),
((useconst) ? "const " : ""),
buf
);
+ if (align > 0) {
+ fprintf(
+ ocfile,
+ "__attribute__(( __aligned__(%d) )) ",
+ align
+ );
+ }
+ fprintf(
+ ocfile,
+ "= {\n "
+ );
int c, col = 1;
while ((c = myfgetc(ifile)) != EOF) {
if (col >= 78 - 6) {
@@ -238,15 +250,22 @@ void process(const char *ifname, const char *ofname, const char *forced_name)
/* print structure */
fprintf(
ohfile,
- "extern %s%sunsigned char %s[];",
+ "extern %s%sunsigned char %s[]",
((usestatic) ? "static " : ""),
((useconst) ? "const " : ""),
buf
);
+ if (align > 0) {
+ fprintf(
+ ohfile,
+ " __attribute__(( __aligned__(%d) ))",
+ align
+ );
+ };
/* print sizeof */
fprintf(
ohfile,
- "\n"
+ ";\n"
"extern %s%ssize_t %s_size;\n",
((usestatic) ? "static " : ""),
((useconst) ? "const " : ""),
@@ -274,7 +293,7 @@ void usage(void)
{
fprintf(
stderr,
- "usage: bin2c [-csvzCH] [-N name] <input_file> <output_file>\n"
+ "usage: bin2c [-csvzCH] [-N name] [-A alignment] <input_file> <output_file>\n"
" <input_file> is the binary file to convert\n"
" <output_file> should not have a .c or .h extension\n"
"\n"
@@ -285,6 +304,7 @@ void usage(void)
" -H - create c-header only\n"
" -C - create c-source file only\n"
" -N - force name of data array\n"
+ " -A - add alignment - parameter can be a hexadecimal or decimal number\n"
);
exit(1);
}
@@ -329,6 +349,20 @@ int main(int argc, char **argv)
name = argv[1];
--argc;
++argv;
+ } else if (!strcmp(argv[1], "-A")) {
+ --argc;
+ ++argv;
+ if (argc <= 1) {
+ fprintf(stderr, "error: -A needs an alignment\n");
+ usage();
+ }
+ align = strtoul(argv[1], NULL, 0);
+ if (align == 0) {
+ fprintf(stderr, "error: Couldn't convert argument of -A\n");
+ usage();
+ }
+ --argc;
+ ++argv;
} else {
usage();
}
diff --git a/misc/tools/config/rtems-boot.ini b/misc/tools/config/rtems-boot.ini
index 4e5d600..d717d83 100644
--- a/misc/tools/config/rtems-boot.ini
+++ b/misc/tools/config/rtems-boot.ini
@@ -52,7 +52,7 @@ arch = arm
vendor = xilinx
board = zynq
config_name = zynq-common
-first_stage = %{ubootdior}/spl/boot.bin
+first_stage = %{ubootdir}/spl/boot.bin
second_state = %{ubootdir}/u-boot.img
kernel_loadaddr = 0x02000000
fdt_loadaddr = 0x08000000
diff --git a/misc/wscript b/misc/wscript
index 21e7f75..5775dcf 100644
--- a/misc/wscript
+++ b/misc/wscript
@@ -92,4 +92,4 @@ def build(bld):
'tools/config/rtems-boot.ini')
def tags(ctx):
- ctx.exec_command('etags $(find . -name \*.[sSch])', shell = True)
+ ctx.exec_command('etags $(find . -name \\*.[sSch])', shell = True)
diff --git a/rtemstoolkit/SimpleIni.h b/rtemstoolkit/SimpleIni.h
index fd37c4b..ec99abb 100644
--- a/rtemstoolkit/SimpleIni.h
+++ b/rtemstoolkit/SimpleIni.h
@@ -3,9 +3,9 @@
<table>
<tr><th>Library <td>SimpleIni
<tr><th>File <td>SimpleIni.h
- <tr><th>Author <td>Brodie Thiesfield [code at jellycan dot com]
+ <tr><th>Author <td>Brodie Thiesfield
<tr><th>Source <td>https://github.com/brofield/simpleini
- <tr><th>Version <td>4.17
+ <tr><th>Version <td>4.20
</table>
Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation.
@@ -20,7 +20,7 @@
@section features FEATURES
- MIT Licence allows free use in all software (including GPL and commercial)
- - multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Windows CE, Linux, Unix)
+ - multi-platform (Windows CE/9x/NT..10/etc, Linux, MacOSX, Unix)
- loading and saving of INI-style configuration files
- configuration files can have any newline format on all platforms
- liberal acceptance of file format
@@ -42,22 +42,31 @@
- Windows/VC6 (warning level 3)
- Windows/VC.NET 2003 (warning level 4)
- Windows/VC 2005 (warning level 4)
+ - Windows/VC 2019 (warning level 4)
- Linux/gcc (-Wall)
@section usage USAGE SUMMARY
+ -# Decide if you will be using utf8 or MBCS files, and working with the
+ data in utf8, wchar_t or ICU chars.
+ -# If you will only be using straight utf8 files and access the data via the
+ char interface, then you do not need any conversion library and could define
+ SI_NO_CONVERSION. Note that no conversion also means no validation of the data.
+ If no converter is specified then the default converter is SI_CONVERT_GENERIC
+ on Mac/Linux and SI_CONVERT_WIN32 on Windows. If you need widechar support on
+ Mac/Linux then use either SI_CONVERT_GENERIC or SI_CONVERT_ICU. These are also
+ supported on all platforms.
-# Define the appropriate symbol for the converter you wish to use and
- include the SimpleIni.h header file. If no specific converter is defined
- then the default converter is used. The default conversion mode uses
- SI_CONVERT_WIN32 on Windows and SI_CONVERT_GENERIC on all other
- platforms. If you are using ICU then SI_CONVERT_ICU is supported on all
- platforms.
- -# Declare an instance the appropriate class. Note that the following
+ include the SimpleIni.h header file.
+ -# Declare an instance of the appropriate class. Note that the following
definitions are just shortcuts for commonly used types. Other types
(PRUnichar, unsigned short, unsigned char) are also possible.
<table>
- <tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef
+ <tr><th>Interface <th>Case-sensitive <th>Load UTF-8 <th>Load MBCS <th>Typedef
+ <tr><th>SI_NO_CONVERSION
+ <tr><td>char <td>No <td>Yes <td>No <td>CSimpleIniA
+ <tr><td>char <td>Yes <td>Yes <td>No <td>CSimpleIniCaseA
<tr><th>SI_CONVERT_GENERIC
<tr><td>char <td>No <td>Yes <td>Yes #1 <td>CSimpleIniA
<tr><td>char <td>Yes <td>Yes <td>Yes <td>CSimpleIniCaseA
@@ -88,6 +97,8 @@
<tr><td>GetValue <td>Return a value for a section & key
<tr><td>SetValue <td>Add or update a value for a section & key
<tr><td>Delete <td>Remove a section, or a key from a section
+ <tr><td>SectionExists <td>Does a section exist?
+ <tr><td>KeyExists <td>Does a key exist?
</table>
-# Call Save() or SaveFile() to save the INI configuration data
@@ -161,9 +172,10 @@
SI_STRLESS class, or by sorting the strings external to this library.
- Usage of the <mbstring.h> header on Windows can be disabled by defining
SI_NO_MBCS. This is defined automatically on Windows CE platforms.
+ - Not thread-safe so manage your own locking
@section contrib CONTRIBUTIONS
-
+
- 2010/05/03: Tobias Gehrig: added GetDoubleValue()
@section licence MIT LICENCE
@@ -213,11 +225,11 @@
#endif
#include <cstring>
+#include <cstdlib>
#include <string>
#include <map>
#include <list>
#include <algorithm>
-#include <stdlib.h>
#include <stdio.h>
#ifdef SI_SUPPORT_IOSTREAMS
@@ -233,16 +245,16 @@
# define SI_ASSERT(x)
#endif
-enum SI_Error {
- SI_OK = 0, //!< No error
- SI_UPDATED = 1, //!< An existing value was updated
- SI_INSERTED = 2, //!< A new value was inserted
+using SI_Error = int;
- // note: test for any error with (retval < 0)
- SI_FAIL = -1, //!< Generic failure
- SI_NOMEM = -2, //!< Out of memory error
- SI_FILE = -3 //!< File error (see errno for detail error)
-};
+constexpr int SI_OK = 0; //!< No error
+constexpr int SI_UPDATED = 1; //!< An existing value was updated
+constexpr int SI_INSERTED = 2; //!< A new value was inserted
+
+// note: test for any error with (retval < 0)
+constexpr int SI_FAIL = -1; //!< Generic failure
+constexpr int SI_NOMEM = -2; //!< Out of memory error
+constexpr int SI_FILE = -3; //!< File error (see errno for detail error)
#define SI_UTF8_SIGNATURE "\xEF\xBB\xBF"
@@ -327,7 +339,7 @@ public:
#endif
/** Strict less ordering by name of key only */
- struct KeyOrder : std::binary_function<Entry, Entry, bool> {
+ struct KeyOrder {
bool operator()(const Entry & lhs, const Entry & rhs) const {
const static SI_STRLESS isLess = SI_STRLESS();
return isLess(lhs.pItem, rhs.pItem);
@@ -335,7 +347,7 @@ public:
};
/** Strict less ordering by order, and then name of key */
- struct LoadOrder : std::binary_function<Entry, Entry, bool> {
+ struct LoadOrder {
bool operator()(const Entry & lhs, const Entry & rhs) const {
if (lhs.nOrder != rhs.nOrder) {
return lhs.nOrder < rhs.nOrder;
@@ -529,7 +541,7 @@ public:
bool IsMultiLine() const { return m_bAllowMultiLine; }
/** Should spaces be added around the equals sign when writing key/value
- pairs out. When true, the result will be "key = value". When false,
+ pairs out. When true, the result will be "key = value". When false,
the result will be "key=value". This value may be changed at any time.
\param a_bSpaces Add spaces around the equals sign?
@@ -540,6 +552,35 @@ public:
/** Query the status of spaces output */
bool UsingSpaces() const { return m_bSpaces; }
+
+
+ /** Should we recognise and parse quotes in single line values?
+
+ \param a_bParseQuotes Parse quoted data in values?
+ */
+ void SetQuotes(bool a_bParseQuotes = true) {
+ m_bParseQuotes = a_bParseQuotes;
+ }
+
+ /** Are we permitting keys and values to be quoted? */
+ bool UsingQuotes() const { return m_bParseQuotes; }
+
+ /** When reading/writing an ini file, do we require every key to have an equals
+ sign to delineate a valid key value. If false, then every valid key must
+ have an equals sign and any lines without an equals sign is ignored. If
+ true then keys do not require an equals sign to be considered a key. Note
+ that this means that any non-commented line of text would become a key.
+
+ \param a_bAllowKeyOnly Permit keys without an equals sign or value.
+ */
+ void SetAllowKeyOnly(bool a_bAllowKeyOnly = true) {
+ m_bAllowKeyOnly = a_bAllowKeyOnly;
+ }
+
+ /** Do we allow keys to exist without a value or equals sign? */
+ bool GetAllowKeyOnly() const { return m_bAllowKeyOnly; }
+
+
/*-----------------------------------------------------------------------*/
/** @}
@@ -770,8 +811,8 @@ public:
) const;
/** Retrieve all unique key names in a section. The sort order of the
- returned strings is NOT DEFINED. You can sort the names into the load
- order if desired. Search this file for ".sort" for an example. Only
+ returned strings is NOT DEFINED. You can sort the names into the load
+ order if desired. Search this file for ".sort" for an example. Only
unique key names are returned.
NOTE! This structure contains only pointers to strings. The actual
@@ -792,8 +833,8 @@ public:
) const;
/** Retrieve all values for a specific key. This method can be used when
- multiple keys are both enabled and disabled. Note that the sort order
- of the returned strings is NOT DEFINED. You can sort the names into
+ multiple keys are both enabled and disabled. Note that the sort order
+ of the returned strings is NOT DEFINED. You can sort the names into
the load order if desired. Search this file for ".sort" for an example.
NOTE! The returned values are pointers to string data stored in memory
@@ -837,13 +878,27 @@ public:
are in use!
@param a_pSection Name of the section to return
- @return boolean Was a section matching the supplied
- name found.
+ @return Section data
*/
const TKeyVal * GetSection(
const SI_CHAR * a_pSection
) const;
+ /** Test if a section exists. Convenience function */
+ inline bool SectionExists(
+ const SI_CHAR * a_pSection
+ ) const {
+ return GetSection(a_pSection) != NULL;
+ }
+
+ /** Test if the key exists in a section. Convenience function. */
+ inline bool KeyExists(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey
+ ) const {
+ return GetValue(a_pSection, a_pKey) != NULL;
+ }
+
/** Retrieve the value for a specific key. If multiple keys are enabled
(see SetMultiKey) then only the first value associated with that key
will be returned, see GetAllValues for getting all values with multikey.
@@ -914,7 +969,7 @@ public:
Strings starting with "t", "y", "on" or "1" are returned as logically true.
Strings starting with "f", "n", "of" or "0" are returned as logically false.
- For all other values the default is returned. Character comparisons are
+ For all other values the default is returned. Character comparisons are
case-insensitive.
@param a_pSection Section to search
@@ -953,9 +1008,9 @@ public:
character starting every line).
@param a_bForceReplace Should all existing values in a multi-key INI
file be replaced with this entry. This option has
- no effect if not using multi-key files. The
+ no effect if not using multi-key files. The
difference between Delete/SetValue and SetValue
- with a_bForceReplace = true, is that the load
+ with a_bForceReplace = true, is that the load
order and comment will be preserved this way.
@return SI_Error See error definitions
@@ -977,19 +1032,19 @@ public:
when multiple keys are enabled.
@param a_pSection Section to add or update
- @param a_pKey Key to add or update.
- @param a_nValue Value to set.
- @param a_pComment Comment to be associated with the key. See the
+ @param a_pKey Key to add or update.
+ @param a_nValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
notes on SetValue() for comments.
- @param a_bUseHex By default the value will be written to the file
- in decimal format. Set this to true to write it
+ @param a_bUseHex By default the value will be written to the file
+ in decimal format. Set this to true to write it
as hexadecimal.
@param a_bForceReplace Should all existing values in a multi-key INI
file be replaced with this entry. This option has
- no effect if not using multi-key files. The
- difference between Delete/SetLongValue and
- SetLongValue with a_bForceReplace = true, is that
- the load order and comment will be preserved this
+ no effect if not using multi-key files. The
+ difference between Delete/SetLongValue and
+ SetLongValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
way.
@return SI_Error See error definitions
@@ -1009,16 +1064,16 @@ public:
when multiple keys are enabled.
@param a_pSection Section to add or update
- @param a_pKey Key to add or update.
- @param a_nValue Value to set.
- @param a_pComment Comment to be associated with the key. See the
+ @param a_pKey Key to add or update.
+ @param a_nValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
notes on SetValue() for comments.
@param a_bForceReplace Should all existing values in a multi-key INI
file be replaced with this entry. This option has
- no effect if not using multi-key files. The
- difference between Delete/SetDoubleValue and
- SetDoubleValue with a_bForceReplace = true, is that
- the load order and comment will be preserved this
+ no effect if not using multi-key files. The
+ difference between Delete/SetDoubleValue and
+ SetDoubleValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
way.
@return SI_Error See error definitions
@@ -1037,16 +1092,16 @@ public:
when multiple keys are enabled.
@param a_pSection Section to add or update
- @param a_pKey Key to add or update.
- @param a_bValue Value to set.
- @param a_pComment Comment to be associated with the key. See the
+ @param a_pKey Key to add or update.
+ @param a_bValue Value to set.
+ @param a_pComment Comment to be associated with the key. See the
notes on SetValue() for comments.
@param a_bForceReplace Should all existing values in a multi-key INI
file be replaced with this entry. This option has
- no effect if not using multi-key files. The
- difference between Delete/SetBoolValue and
- SetBoolValue with a_bForceReplace = true, is that
- the load order and comment will be preserved this
+ no effect if not using multi-key files. The
+ difference between Delete/SetBoolValue and
+ SetBoolValue with a_bForceReplace = true, is that
+ the load order and comment will be preserved this
way.
@return SI_Error See error definitions
@@ -1065,8 +1120,8 @@ public:
data returned by GetSection is invalid and must not be used after
anything has been deleted from that section using this method.
Note when multiple keys is enabled, this will delete all keys with
- that name; there is no way to selectively delete individual key/values
- in this situation.
+ that name; to selectively delete individual key/values, use
+ DeleteValue.
@param a_pSection Section to delete key from, or if
a_pKey is NULL, the section to remove.
@@ -1085,6 +1140,33 @@ public:
bool a_bRemoveEmpty = false
);
+ /** Delete an entire section, or a key from a section. If value is
+ provided, only remove keys with the value. Note that the data
+ returned by GetSection is invalid and must not be used after
+ anything has been deleted from that section using this method.
+ Note when multiple keys is enabled, all keys with the value will
+ be deleted.
+
+ @param a_pSection Section to delete key from, or if
+ a_pKey is NULL, the section to remove.
+ @param a_pKey Key to remove from the section. Set to
+ NULL to remove the entire section.
+ @param a_pValue Value of key to remove from the section.
+ Set to NULL to remove all keys.
+ @param a_bRemoveEmpty If the section is empty after this key has
+ been deleted, should the empty section be
+ removed?
+
+ @return true Key/value or section was deleted.
+ @return false Key/value or section was not found.
+ */
+ bool DeleteValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ bool a_bRemoveEmpty = false
+ );
+
/*-----------------------------------------------------------------------*/
/** @}
@{ @name Converter */
@@ -1140,9 +1222,9 @@ private:
comment character starting every line).
@param a_bForceReplace Should all existing values in a multi-key INI
file be replaced with this entry. This option has
- no effect if not using multi-key files. The
+ no effect if not using multi-key files. The
difference between Delete/AddEntry and AddEntry
- with a_bForceReplace = true, is that the load
+ with a_bForceReplace = true, is that the load
order and comment will be preserved this way.
@param a_bCopyStrings Should copies of the strings be made or not.
If false then the pointers will be used as is.
@@ -1186,6 +1268,7 @@ private:
bool IsMultiLineTag(const SI_CHAR * a_pData) const;
bool IsMultiLineData(const SI_CHAR * a_pData) const;
+ bool IsSingleLineQuotedValue(const SI_CHAR* a_pData) const;
bool LoadMultiLineText(
SI_CHAR *& a_pData,
const SI_CHAR *& a_pVal,
@@ -1217,6 +1300,9 @@ private:
/** File comment for this data, if one exists. */
const SI_CHAR * m_pFileComment;
+ /** constant empty string */
+ const SI_CHAR m_cEmptyString;
+
/** Parsed INI data. Section -> (Key -> Value). */
TSection m_data;
@@ -1237,6 +1323,12 @@ private:
/** Should spaces be written out surrounding the equals sign? */
bool m_bSpaces;
+
+ /** Should quoted data in values be recognized and parsed? */
+ bool m_bParseQuotes;
+
+ /** Do keys always need to have an equals sign when reading/writing? */
+ bool m_bAllowKeyOnly;
/** Next order value, used to ensure sections and keys are output in the
same order that they are loaded/added.
@@ -1257,10 +1349,13 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CSimpleIniTempl(
: m_pData(0)
, m_uDataLen(0)
, m_pFileComment(NULL)
+ , m_cEmptyString(0)
, m_bStoreIsUtf8(a_bIsUtf8)
, m_bAllowMultiKey(a_bAllowMultiKey)
, m_bAllowMultiLine(a_bAllowMultiLine)
, m_bSpaces(true)
+ , m_bParseQuotes(false)
+ , m_bAllowKeyOnly(false)
, m_nOrder(0)
{ }
@@ -1357,14 +1452,14 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadFile(
if (lSize == 0) {
return SI_OK;
}
-
+
// allocate and ensure NULL terminated
- char * pData = new char[lSize+1];
+ char * pData = new(std::nothrow) char[lSize+static_cast<size_t>(1)];
if (!pData) {
return SI_NOMEM;
}
pData[lSize] = 0;
-
+
// load data into buffer
fseek(a_fpFile, 0, SEEK_SET);
size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile);
@@ -1386,21 +1481,26 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
size_t a_uDataLen
)
{
- SI_CONVERTER converter(m_bStoreIsUtf8);
-
- if (a_uDataLen == 0) {
+ if (!a_pData) {
return SI_OK;
}
+
+ // if the UTF-8 BOM exists, consume it and set mode to unicode, if we have
+ // already loaded data and try to change mode half-way through then this will
+ // be ignored and we will assert in debug versions
+ if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
+ a_pData += 3;
+ a_uDataLen -= 3;
+ SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data
+ SetUnicode();
+ }
- // consume the UTF-8 BOM if it exists
- if (m_bStoreIsUtf8 && a_uDataLen >= 3) {
- if (memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) {
- a_pData += 3;
- a_uDataLen -= 3;
- }
+ if (a_uDataLen == 0) {
+ return SI_OK;
}
// determine the length of the converted data
+ SI_CONVERTER converter(m_bStoreIsUtf8);
size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen);
if (uLen == (size_t)(-1)) {
return SI_FAIL;
@@ -1408,7 +1508,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadData(
// allocate memory for the data, ensure that there is a NULL
// terminator wherever the converted data ends
- SI_CHAR * pData = new SI_CHAR[uLen+1];
+ SI_CHAR * pData = new(std::nothrow) SI_CHAR[uLen+1];
if (!pData) {
return SI_NOMEM;
}
@@ -1512,6 +1612,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
{
a_pComment = NULL;
+ bool bHaveValue = false;
SI_CHAR * pTrail = NULL;
while (*a_pData) {
// skip spaces and empty lines
@@ -1569,19 +1670,20 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
}
// find the end of the key name (it may contain spaces)
- // and convert it to lowercase as necessary
a_pKey = a_pData;
while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) {
++a_pData;
}
+ // *a_pData is null, equals, or newline
- // if it's an invalid line, just skip it
- if (*a_pData != '=') {
+ // if no value and we don't allow no value, then invalid
+ bHaveValue = (*a_pData == '=');
+ if (!bHaveValue && !m_bAllowKeyOnly) {
continue;
}
// empty keys are invalid
- if (a_pKey == a_pData) {
+ if (bHaveValue && a_pKey == a_pData) {
while (*a_pData && !IsNewLineChar(*a_pData)) {
++a_pData;
}
@@ -1594,36 +1696,56 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::FindEntry(
--pTrail;
}
++pTrail;
- *pTrail = 0;
- // skip leading whitespace on the value
- ++a_pData; // safe as checked that it == '=' above
- while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
- ++a_pData;
- }
+ if (bHaveValue) {
+ // process the value
+ *pTrail = 0;
- // find the end of the value which is the end of this line
- a_pVal = a_pData;
- while (*a_pData && !IsNewLineChar(*a_pData)) {
- ++a_pData;
- }
+ // skip leading whitespace on the value
+ ++a_pData; // safe as checked that it == '=' above
+ while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) {
+ ++a_pData;
+ }
- // remove trailing spaces from the value
- pTrail = a_pData - 1;
- if (*a_pData) { // prepare for the next round
- SkipNewLine(a_pData);
- }
- while (pTrail >= a_pVal && IsSpace(*pTrail)) {
- --pTrail;
- }
- ++pTrail;
- *pTrail = 0;
+ // find the end of the value which is the end of this line
+ a_pVal = a_pData;
+ while (*a_pData && !IsNewLineChar(*a_pData)) {
+ ++a_pData;
+ }
+
+ // remove trailing spaces from the value
+ pTrail = a_pData - 1;
+ if (*a_pData) { // prepare for the next round
+ SkipNewLine(a_pData);
+ }
+ while (pTrail >= a_pVal && IsSpace(*pTrail)) {
+ --pTrail;
+ }
+ ++pTrail;
+ *pTrail = 0;
- // check for multi-line entries
- if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
- // skip the "<<<" to get the tag that will end the multiline
- const SI_CHAR * pTagName = a_pVal + 3;
- return LoadMultiLineText(a_pData, a_pVal, pTagName);
+ // check for multi-line entries
+ if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) {
+ // skip the "<<<" to get the tag that will end the multiline
+ const SI_CHAR* pTagName = a_pVal + 3;
+ return LoadMultiLineText(a_pData, a_pVal, pTagName);
+ }
+
+ // check for quoted values, we are not supporting escapes in quoted values (yet)
+ if (m_bParseQuotes) {
+ --pTrail;
+ if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') {
+ ++a_pVal;
+ *pTrail = 0;
+ }
+ }
+ }
+ else {
+ // no value to process, just prepare for the next
+ if (*a_pData) {
+ SkipNewLine(a_pData);
+ }
+ *pTrail = 0;
}
// return the standard entry
@@ -1685,6 +1807,41 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsMultiLineData(
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
bool
+CSimpleIniTempl<SI_CHAR, SI_STRLESS, SI_CONVERTER>::IsSingleLineQuotedValue(
+ const SI_CHAR* a_pData
+) const
+{
+ // data needs quoting if it starts or ends with whitespace
+ // and doesn't have embedded newlines
+
+ // empty string
+ if (!*a_pData) {
+ return false;
+ }
+
+ // check for prefix
+ if (IsSpace(*a_pData)) {
+ return true;
+ }
+
+ // embedded newlines
+ while (*a_pData) {
+ if (IsNewLineChar(*a_pData)) {
+ return false;
+ }
+ ++a_pData;
+ }
+
+ // check for suffix
+ if (IsSpace(*--a_pData)) {
+ return true;
+ }
+
+ return false;
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::IsNewLineChar(
SI_CHAR a_c
) const
@@ -1716,8 +1873,8 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
a_pVal = a_pData;
// find the end tag. This tag must start in column 1 and be
- // followed by a newline. No whitespace removal is done while
- // searching for this tag.
+ // followed by a newline. We ignore any whitespace after the end
+ // tag but not whitespace before it.
SI_CHAR cEndOfLineChar = *a_pData;
for(;;) {
// if we are loading comments then we need a comment character as
@@ -1773,10 +1930,18 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::LoadMultiLineText(
// if are looking for a tag then do the check now. This is done before
// checking for end of the data, so that if we have the tag at the end
// of the data then the tag is removed correctly.
- if (a_pTagName &&
- (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)))
- {
- break;
+ if (a_pTagName) {
+ // strip whitespace from the end of this tag
+ SI_CHAR* pc = a_pData - 1;
+ while (pc > pDataLine && IsSpace(*pc)) --pc;
+ SI_CHAR ch = *++pc;
+ *pc = 0;
+
+ if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) {
+ break;
+ }
+
+ *pc = ch;
}
// if we are at the end of the data then we just automatically end
@@ -1833,7 +1998,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::CopyString(
for ( ; a_pString[uLen]; ++uLen) /*loop*/ ;
}
++uLen; // NULL character
- SI_CHAR * pCopy = new SI_CHAR[uLen];
+ SI_CHAR * pCopy = new(std::nothrow) SI_CHAR[uLen];
if (!pCopy) {
return SI_NOMEM;
}
@@ -1878,7 +2043,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
// only set the comment if this is a section only entry
Entry oSection(a_pSection, ++m_nOrder);
- if (a_pComment && (!a_pKey || !a_pValue)) {
+ if (a_pComment && !a_pKey) {
oSection.pComment = a_pComment;
}
@@ -1888,14 +2053,15 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
iSection = i.first;
bInserted = true;
}
- if (!a_pKey || !a_pValue) {
- // section only entries are specified with pItem and pVal as NULL
+ if (!a_pKey) {
+ // section only entries are specified with pItem as NULL
return bInserted ? SI_INSERTED : SI_UPDATED;
}
// check for existence of the key
TKeyVal & keyval = iSection->second;
typename TKeyVal::iterator iKey = keyval.find(a_pKey);
+ bInserted = iKey == keyval.end();
// remove all existing entries but save the load order and
// comment of the first entry
@@ -1918,6 +2084,11 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
iKey = keyval.end();
}
+ // values need to be a valid string, even if they are an empty string
+ if (!a_pValue) {
+ a_pValue = &m_cEmptyString;
+ }
+
// make string copies if necessary
bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace;
if (a_bCopyStrings) {
@@ -1942,8 +2113,8 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::AddEntry(
}
typename TKeyVal::value_type oEntry(oKey, static_cast<const SI_CHAR *>(NULL));
iKey = keyval.insert(oEntry);
- bInserted = true;
}
+
iKey->second = a_pValue;
return bInserted ? SI_INSERTED : SI_UPDATED;
}
@@ -2009,23 +2180,23 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetLongValue(
long nValue = a_nDefault;
char * pszSuffix = szValue;
if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) {
- if (!szValue[2]) return a_nDefault;
- nValue = ::strtol(&szValue[2], &pszSuffix, 16);
+ if (!szValue[2]) return a_nDefault;
+ nValue = strtol(&szValue[2], &pszSuffix, 16);
}
else {
- nValue = ::strtol(szValue, &pszSuffix, 10);
+ nValue = strtol(szValue, &pszSuffix, 10);
}
// any invalid strings will return the default value
- if (*pszSuffix) {
- return a_nDefault;
+ if (*pszSuffix) {
+ return a_nDefault;
}
return nValue;
}
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
-SI_Error
+SI_Error
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
const SI_CHAR * a_pSection,
const SI_CHAR * a_pKey,
@@ -2043,13 +2214,13 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetLongValue(
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
#else // !__STDC_WANT_SECURE_LIB__
- sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue);
+ snprintf(szInput, sizeof(szInput), a_bUseHex ? "0x%lx" : "%ld", a_nValue);
#endif // __STDC_WANT_SECURE_LIB__
// convert to output text
SI_CHAR szOutput[64];
SI_CONVERTER c(m_bStoreIsUtf8);
- c.ConvertFromStore(szInput, strlen(szInput) + 1,
+ c.ConvertFromStore(szInput, strlen(szInput) + 1,
szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
// actually add it
@@ -2080,42 +2251,42 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetDoubleValue(
double nValue = strtod(szValue, &pszSuffix);
// any invalid strings will return the default value
- if (!pszSuffix || *pszSuffix) {
- return a_nDefault;
+ if (!pszSuffix || *pszSuffix) {
+ return a_nDefault;
}
return nValue;
}
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
-SI_Error
+SI_Error
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetDoubleValue(
- const SI_CHAR * a_pSection,
- const SI_CHAR * a_pKey,
- double a_nValue,
- const SI_CHAR * a_pComment,
- bool a_bForceReplace
- )
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ double a_nValue,
+ const SI_CHAR * a_pComment,
+ bool a_bForceReplace
+ )
{
- // use SetValue to create sections
- if (!a_pSection || !a_pKey) return SI_FAIL;
+ // use SetValue to create sections
+ if (!a_pSection || !a_pKey) return SI_FAIL;
- // convert to an ASCII string
- char szInput[64];
+ // convert to an ASCII string
+ char szInput[64];
#if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE
- sprintf_s(szInput, "%f", a_nValue);
+ sprintf_s(szInput, "%f", a_nValue);
#else // !__STDC_WANT_SECURE_LIB__
- sprintf(szInput, "%f", a_nValue);
+ snprintf(szInput, sizeof(szInput), "%f", a_nValue);
#endif // __STDC_WANT_SECURE_LIB__
- // convert to output text
- SI_CHAR szOutput[64];
- SI_CONVERTER c(m_bStoreIsUtf8);
- c.ConvertFromStore(szInput, strlen(szInput) + 1,
- szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
+ // convert to output text
+ SI_CHAR szOutput[64];
+ SI_CONVERTER c(m_bStoreIsUtf8);
+ c.ConvertFromStore(szInput, strlen(szInput) + 1,
+ szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
- // actually add it
- return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
+ // actually add it
+ return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
}
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
@@ -2154,7 +2325,7 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetBoolValue(
}
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
-SI_Error
+SI_Error
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(
const SI_CHAR * a_pSection,
const SI_CHAR * a_pKey,
@@ -2172,13 +2343,13 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::SetBoolValue(
// convert to output text
SI_CHAR szOutput[64];
SI_CONVERTER c(m_bStoreIsUtf8);
- c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
+ c.ConvertFromStore(pszInput, strlen(pszInput) + 1,
szOutput, sizeof(szOutput) / sizeof(SI_CHAR));
// actually add it
return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true);
}
-
+
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
bool
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllValues(
@@ -2390,6 +2561,19 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
oSections.sort(typename Entry::LoadOrder());
#endif
+ // if there is an empty section name, then it must be written out first
+ // regardless of the load order
+ typename TNamesDepend::iterator is = oSections.begin();
+ for (; is != oSections.end(); ++is) {
+ if (!*is->pItem) {
+ // move the empty section name to the front of the section list
+ if (is != oSections.begin()) {
+ oSections.splice(oSections.begin(), oSections, is, std::next(is));
+ }
+ break;
+ }
+ }
+
// write the file comment if we have one
bool bNeedNewLine = false;
if (m_pFileComment) {
@@ -2465,22 +2649,31 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Save(
}
a_oOutput.Write(convert.Data());
- // write the value
- if (!convert.ConvertToStore(iValue->pItem)) {
- return SI_FAIL;
- }
- a_oOutput.Write(m_bSpaces ? " = " : "=");
- if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
- // multi-line data needs to be processed specially to ensure
- // that we use the correct newline format for the current system
- a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
- if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
+ // write the value as long
+ if (*iValue->pItem || !m_bAllowKeyOnly) {
+ if (!convert.ConvertToStore(iValue->pItem)) {
return SI_FAIL;
}
- a_oOutput.Write("END_OF_TEXT");
- }
- else {
- a_oOutput.Write(convert.Data());
+ a_oOutput.Write(m_bSpaces ? " = " : "=");
+ if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) {
+ // the only way to preserve external whitespace on a value (i.e. before or after)
+ // is to quote it. This is simple quoting, we don't escape quotes within the data.
+ a_oOutput.Write("\"");
+ a_oOutput.Write(convert.Data());
+ a_oOutput.Write("\"");
+ }
+ else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) {
+ // multi-line data needs to be processed specially to ensure
+ // that we use the correct newline format for the current system
+ a_oOutput.Write("<<<END_OF_TEXT" SI_NEWLINE_A);
+ if (!OutputMultiLineText(a_oOutput, convert, iValue->pItem)) {
+ return SI_FAIL;
+ }
+ a_oOutput.Write("END_OF_TEXT");
+ }
+ else {
+ a_oOutput.Write(convert.Data());
+ }
}
a_oOutput.Write(SI_NEWLINE_A);
}
@@ -2529,6 +2722,18 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
bool a_bRemoveEmpty
)
{
+ return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty);
+}
+
+template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
+bool
+CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteValue(
+ const SI_CHAR * a_pSection,
+ const SI_CHAR * a_pKey,
+ const SI_CHAR * a_pValue,
+ bool a_bRemoveEmpty
+ )
+{
if (!a_pSection) {
return false;
}
@@ -2545,18 +2750,30 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::Delete(
return false;
}
+ const static SI_STRLESS isLess = SI_STRLESS();
+
// remove any copied strings and then the key
typename TKeyVal::iterator iDelete;
+ bool bDeleted = false;
do {
iDelete = iKeyVal++;
- DeleteString(iDelete->first.pItem);
- DeleteString(iDelete->second);
- iSection->second.erase(iDelete);
+ if(a_pValue == NULL ||
+ (isLess(a_pValue, iDelete->second) == false &&
+ isLess(iDelete->second, a_pValue) == false)) {
+ DeleteString(iDelete->first.pItem);
+ DeleteString(iDelete->second);
+ iSection->second.erase(iDelete);
+ bDeleted = true;
+ }
}
while (iKeyVal != iSection->second.end()
&& !IsLess(a_pKey, iKeyVal->first.pItem));
+ if(!bDeleted) {
+ return false;
+ }
+
// done now if the section is not empty or we are not pruning away
// the empty sections. Otherwise let it fall through into the section
// deletion code
@@ -2610,13 +2827,15 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::DeleteString(
// SimpleIni.h, set the converter that you wish you use by defining one of the
// following symbols.
//
+// SI_NO_CONVERSION Do not make the "W" wide character version of the
+// library available. Only CSimpleIniA etc is defined.
// SI_CONVERT_GENERIC Use the Unicode reference conversion library in
// the accompanying files ConvertUTF.h/c
// SI_CONVERT_ICU Use the IBM ICU conversion library. Requires
// ICU headers on include path and icuuc.lib
// SI_CONVERT_WIN32 Use the Win32 API functions for conversion.
-#if !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
+#if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU)
# ifdef _WIN32
# define SI_CONVERT_WIN32
# else
@@ -2873,7 +3092,7 @@ public:
// This uses the Unicode reference implementation to do the
// conversion from UTF-8 to wchar_t. The required files are
// ConvertUTF.h and ConvertUTF.c which should be included in
- // the distribution but are publically available from unicode.org
+ // the distribution but are publicly available from unicode.org
// at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
ConversionResult retval;
const UTF8 * pUtf8 = (const UTF8 *) a_pInputData;
@@ -2960,7 +3179,7 @@ public:
// This uses the Unicode reference implementation to do the
// conversion from wchar_t to UTF-8. The required files are
// ConvertUTF.h and ConvertUTF.c which should be included in
- // the distribution but are publically available from unicode.org
+ // the distribution but are publicly available from unicode.org
// at http://www.unicode.org/Public/PROGRAMS/CVTUTF/
ConversionResult retval;
UTF8 * pUtf8 = (UTF8 *) a_pOutputData;
@@ -3346,6 +3565,19 @@ public:
#endif // SI_CONVERT_WIN32
+
+// ---------------------------------------------------------------------------
+// SI_NO_CONVERSION
+// ---------------------------------------------------------------------------
+#ifdef SI_NO_CONVERSION
+
+#define SI_Case SI_GenericCase
+#define SI_NoCase SI_GenericNoCase
+
+#endif // SI_NO_CONVERSION
+
+
+
// ---------------------------------------------------------------------------
// TYPE DEFINITIONS
// ---------------------------------------------------------------------------
@@ -3355,27 +3587,35 @@ typedef CSimpleIniTempl<char,
typedef CSimpleIniTempl<char,
SI_Case<char>,SI_ConvertA<char> > CSimpleIniCaseA;
-#if defined(SI_CONVERT_ICU)
+#if defined(SI_NO_CONVERSION)
+// if there is no wide char conversion then we don't need to define the
+// widechar "W" versions of CSimpleIni
+# define CSimpleIni CSimpleIniA
+# define CSimpleIniCase CSimpleIniCaseA
+# define SI_NEWLINE SI_NEWLINE_A
+#else
+# if defined(SI_CONVERT_ICU)
typedef CSimpleIniTempl<UChar,
SI_NoCase<UChar>,SI_ConvertW<UChar> > CSimpleIniW;
typedef CSimpleIniTempl<UChar,
SI_Case<UChar>,SI_ConvertW<UChar> > CSimpleIniCaseW;
-#else
+# else
typedef CSimpleIniTempl<wchar_t,
SI_NoCase<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniW;
typedef CSimpleIniTempl<wchar_t,
SI_Case<wchar_t>,SI_ConvertW<wchar_t> > CSimpleIniCaseW;
-#endif
+# endif
-#ifdef _UNICODE
-# define CSimpleIni CSimpleIniW
-# define CSimpleIniCase CSimpleIniCaseW
-# define SI_NEWLINE SI_NEWLINE_W
-#else // !_UNICODE
-# define CSimpleIni CSimpleIniA
-# define CSimpleIniCase CSimpleIniCaseA
-# define SI_NEWLINE SI_NEWLINE_A
-#endif // _UNICODE
+# ifdef _UNICODE
+# define CSimpleIni CSimpleIniW
+# define CSimpleIniCase CSimpleIniCaseW
+# define SI_NEWLINE SI_NEWLINE_W
+# else // !_UNICODE
+# define CSimpleIni CSimpleIniA
+# define CSimpleIniCase CSimpleIniCaseA
+# define SI_NEWLINE SI_NEWLINE_A
+# endif // _UNICODE
+#endif
#ifdef _MSC_VER
# pragma warning (pop)
diff --git a/rtemstoolkit/config.py b/rtemstoolkit/config.py
index be100f2..8fd4a32 100644
--- a/rtemstoolkit/config.py
+++ b/rtemstoolkit/config.py
@@ -127,7 +127,7 @@ class file(object):
self.opts.set_dry_run()
def _label(self, name):
- if name.startswith('%{') and name[-1] is '}':
+ if name.startswith('%{') and name[-1] == '}':
return name
return '%{' + name.lower() + '}'
diff --git a/rtemstoolkit/configuration.py b/rtemstoolkit/configuration.py
index 1f57de4..ba38104 100644
--- a/rtemstoolkit/configuration.py
+++ b/rtemstoolkit/configuration.py
@@ -57,7 +57,7 @@ class configuration:
else:
self.config = configparser.ConfigParser()
self.ini = None
- self.macro_filter = re.compile('\$\{[^\}]+\}')
+ self.macro_filter = re.compile(r'\$\{[^\}]+\}')
def __str__(self):
if self.ini is None:
diff --git a/rtemstoolkit/elftoolchain/libelf/_libelf_config.h b/rtemstoolkit/elftoolchain/libelf/_libelf_config.h
index 102aa01..16b5f0b 100644
--- a/rtemstoolkit/elftoolchain/libelf/_libelf_config.h
+++ b/rtemstoolkit/elftoolchain/libelf/_libelf_config.h
@@ -23,28 +23,14 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: _libelf_config.h 3566 2017-08-31 02:28:40Z emaste $
+ * $Id: _libelf_config.h 3764 2019-06-28 21:44:46Z emaste $
*/
-#if defined(__APPLE__) || defined(__DragonFly__)
-
-#if defined(__amd64__)
-#define LIBELF_ARCH EM_X86_64
-#define LIBELF_BYTEORDER ELFDATA2LSB
-#define LIBELF_CLASS ELFCLASS64
-#elif defined(__i386__)
-#define LIBELF_ARCH EM_386
-#define LIBELF_BYTEORDER ELFDATA2LSB
-#define LIBELF_CLASS ELFCLASS32
-#endif
-
-#endif /* __DragonFly__ */
-
-#ifdef __FreeBSD__
+#if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__)
/*
* Define LIBELF_{ARCH,BYTEORDER,CLASS} based on the machine architecture.
- * See also: <machine/elf.h>.
+ * See also: <machine/elf.h> on FreeBSD.
*/
#if defined(__amd64__)
@@ -91,6 +77,16 @@
#endif
#define LIBELF_CLASS ELFCLASS32
+#elif defined(__powerpc64__)
+
+#define LIBELF_ARCH EM_PPC64
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define LIBELF_BYTEORDER ELFDATA2LSB
+#else
+#define LIBELF_BYTEORDER ELFDATA2MSB
+#endif
+#define LIBELF_CLASS ELFCLASS64
+
#elif defined(__powerpc__)
#define LIBELF_ARCH EM_PPC
@@ -103,6 +99,12 @@
#define LIBELF_BYTEORDER ELFDATA2LSB
#define LIBELF_CLASS ELFCLASS64
+#elif defined(__riscv64)
+
+#define LIBELF_ARCH EM_RISCV
+#define LIBELF_BYTEORDER ELFDATA2LSB
+#define LIBELF_CLASS ELFCLASS64
+
#elif defined(__sparc__)
#define LIBELF_ARCH EM_SPARCV9
@@ -110,9 +112,9 @@
#define LIBELF_CLASS ELFCLASS64
#else
-#error Unknown FreeBSD architecture.
+#error Unknown architecture.
#endif
-#endif /* __FreeBSD__ */
+#endif /* defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) */
/*
* Definitions for Minix3.
diff --git a/rtemstoolkit/execute.py b/rtemstoolkit/execute.py
index ed81589..31d3a8a 100755
--- a/rtemstoolkit/execute.py
+++ b/rtemstoolkit/execute.py
@@ -40,6 +40,7 @@ import functools
import io
import os
import re
+import shlex
import sys
import subprocess
import threading
@@ -124,6 +125,14 @@ class execute(object):
self.timing_out = False
self.proc = None
+ @staticmethod
+ def _shlex_join(elements):
+ try:
+ return shlex.join(elements)
+ except AttributeError:
+ # Python older than 3.8 does not have shlex.join
+ return ' '.join(elements)
+
def capture(self, proc, command = 'pipe', timeout = None):
"""Create 3 threads to read stdout and stderr and send to the output handler
and call an input handler is provided. Based on the 'communicate' code
@@ -355,10 +364,17 @@ class execute(object):
a string."""
if self.output is None:
raise error.general('capture needs an output handler')
- cs = command
- if type(command) is list:
- def add(x, y): return x + ' ' + str(y)
- cs = functools.reduce(add, command, '')[1:]
+ # If a string split and not a shell command split
+ if not shell and isinstance(command, str):
+ command = shlex.split(command)
+ if shell and isinstance(command, list):
+ command = execute._shlex_join(command)
+ if self.shell_exe:
+ command = self.shell_exe + ' ' + command
+ if isinstance(command, list):
+ cs = execute._shlex_join(command)
+ else:
+ cs = command
what = 'spawn'
if shell:
what = 'shell'
@@ -366,9 +382,6 @@ class execute(object):
if self.verbose:
log.output(what + ': ' + cs)
log.trace('exe: %s' % (cs))
- if shell and self.shell_exe:
- command = arg_list(command)
- command[:0] = self.shell_exe
if not stdin and self.input:
stdin = subprocess.PIPE
if not stdout:
@@ -389,11 +402,50 @@ class execute(object):
r, e = os.path.splitext(command[0])
if e not in ['.exe', '.com', '.bat']:
command[0] = command[0] + '.exe'
- proc = subprocess.Popen(command, shell = shell,
- cwd = cwd, env = env,
- stdin = stdin, stdout = stdout,
- stderr = stderr,
- close_fds = False)
+ pipe_commands = []
+ if shell:
+ pipe_commands.append(command)
+ else:
+ # See if there is a pipe operator in the command. If present
+ # split the commands by the pipe and run them with the pipe.
+ # if no pipe it is the normal stdin and stdout
+ current_command = []
+ for cmd in command:
+ if cmd == '|':
+ pipe_commands.append(current_command)
+ current_command = []
+ else:
+ current_command.append(cmd)
+ pipe_commands.append(current_command)
+ proc = None
+ if len(pipe_commands) == 1:
+ cmd = pipe_commands[0]
+ proc = subprocess.Popen(
+ cmd, shell = shell,
+ cwd = cwd, env = env,
+ stdin = stdin, stdout = stdout,
+ stderr = stderr,
+ close_fds = False)
+ else:
+ for i, cmd in enumerate(pipe_commands):
+ if i == 0:
+ proc = subprocess.Popen(
+ cmd, shell=shell,
+ cwd=cwd, env=env,
+ stdin=stdin, stdout=subprocess.PIPE)
+ elif i == len(pipe_commands) - 1:
+ proc = subprocess.Popen(
+ cmd, shell=shell,
+ cwd=cwd, env=env,
+ stdin=proc.stdout,
+ stdout=stdout, stderr=stderr,
+ close_fds=False)
+ else:
+ proc = subprocess.Popen(
+ cmd, shell=shell,
+ cwd=cwd, env=env,
+ stdin=proc.stdout,
+ stdout=subprocess.PIPE)
if not capture:
return (0, proc)
if self.output is None:
@@ -560,16 +612,28 @@ class capture_execution(execute):
if __name__ == "__main__":
def run_tests(e, commands, use_shell):
for c in commands['shell']:
- e.shell(c)
+ ec, out = e.shell(c)
+ if ec != 0:
+ raise RuntimeError('ec = {}'.format(ec))
+ for c in commands['error']:
+ ec, out = e.shell(c)
+ if ec == 0:
+ raise RuntimeError('ec = {}'.format(ec))
for c in commands['spawn']:
- e.spawn(c)
+ ec, out = e.spawn(c)
+ if ec != 0:
+ raise RuntimeError('ec = {}'.format(ec))
for c in commands['cmd']:
if type(c) is str:
- e.command(c, shell = use_shell)
+ ec, out = e.command(c, shell = use_shell)
else:
- e.command(c[0], c[1], shell = use_shell)
+ ec, out = e.command(c[0], c[1], shell = use_shell)
+ if ec != 0:
+ raise RuntimeError('ec = {}'.format(ec))
for c in commands['csubsts']:
- e.command_subst(c[0], c[1], shell = use_shell)
+ ec, out = e.command_subst(c[0], c[1], shell = use_shell)
+ if ec != 0:
+ raise RuntimeError('ec = {}'.format(ec))
ec, proc = e.command(commands['pipe'][0], commands['pipe'][1],
capture = False, stdin = subprocess.PIPE)
if ec == 0:
@@ -583,6 +647,15 @@ if __name__ == "__main__":
proc.stdin.close()
e.capture(proc)
del proc
+ else:
+ raise RuntimeError('ec = {}'.format(ec))
+ for c in commands['open']:
+ ec, proc = e.open(c)
+ if ec == 0:
+ e.capture(proc)
+ del proc
+ else:
+ raise RuntimeError('ec = {}'.format(ec))
def capture_output(text):
print(text, end = '')
@@ -595,19 +668,29 @@ if __name__ == "__main__":
commands = {}
commands['windows'] = {}
commands['unix'] = {}
- commands['windows']['shell'] = ['cd', 'dir /w', '.\\xyz', cmd_shell_test]
+ commands['windows']['shell'] = ['cd', 'dir /w', cmd_shell_test]
+ commands['windows']['error'] = ['.\\xyz']
commands['windows']['spawn'] = ['hostname', 'hostnameZZ', ['netstat', '/e']]
commands['windows']['cmd'] = [('ipconfig'), ('nslookup', 'www.python.org')]
commands['windows']['csubsts'] = [('netstat %0', ['-a']),
('netstat %0 %1', ['-a', '-n'])]
commands['windows']['pipe'] = ('ftp', None, 'help\nquit')
- commands['unix']['shell'] = ['pwd', 'ls -las', './xyz', sh_shell_test]
- commands['unix']['spawn'] = ['ls', 'execute.pyc', ['ls', '-i']]
+ commands['windows']['open'] = [
+ ["echo", "hello rtems", "|", "findstr", "rtems"],
+ " ".join(["echo", "hello rtems", "|", "findstr", "rtems"])
+ ]
+ commands['unix']['shell'] = ['pwd', 'ls -las', sh_shell_test, 'ls -las']
+ commands['unix']['error'] = ['./xyz']
+ commands['unix']['spawn'] = ['ls', ['ls', '-i'], 'ls -l']
commands['unix']['cmd'] = [('date'), ('date', '-R'), ('date', ['-u', '+%d %D']),
('date', '-u "+%d %D %S"')]
commands['unix']['csubsts'] = [('date %0 "+%d %D %S"', ['-u']),
('date %0 %1', ['-u', '+%d %D %S'])]
commands['unix']['pipe'] = ('grep', 'hello', 'hello world')
+ commands['unix']['open'] = [
+ ["echo", "hello world", "|", "cut", "-d ", "-f2"],
+ " ".join(["echo", "hello world", "|", "cut", "-d\" \"", "-f2"])
+ ]
print(arg_list('cmd a1 a2 "a3 is a string" a4'))
print(arg_list('cmd b1 b2 "b3 is a string a4'))
diff --git a/rtemstoolkit/libiberty/argv.c b/rtemstoolkit/libiberty/argv.c
new file mode 100644
index 0000000..a95a10e
--- /dev/null
+++ b/rtemstoolkit/libiberty/argv.c
@@ -0,0 +1,568 @@
+/* Create and destroy argument vectors (argv's)
+ Copyright (C) 1992-2023 Free Software Foundation, Inc.
+ Written by Fred Fish @ Cygnus Support
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+
+/* Create and destroy argument vectors. An argument vector is simply an
+ array of string pointers, terminated by a NULL pointer. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "ansidecl.h"
+#include "libiberty.h"
+#include "safe-ctype.h"
+
+/* Routines imported from standard C runtime libraries. */
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef EOS
+#define EOS '\0'
+#endif
+
+#define INITIAL_MAXARGC 8 /* Number of args + NULL in initial argv */
+
+
+/*
+
+@deftypefn Extension char** dupargv (char * const *@var{vector})
+
+Duplicate an argument vector. Simply scans through @var{vector},
+duplicating each argument until the terminating @code{NULL} is found.
+Returns a pointer to the argument vector if successful. Returns
+@code{NULL} if there is insufficient memory to complete building the
+argument vector.
+
+@end deftypefn
+
+*/
+
+char **
+dupargv (char * const *argv)
+{
+ int argc;
+ char **copy;
+
+ if (argv == NULL)
+ return NULL;
+
+ /* the vector */
+ for (argc = 0; argv[argc] != NULL; argc++);
+ copy = (char **) xmalloc ((argc + 1) * sizeof (char *));
+
+ /* the strings */
+ for (argc = 0; argv[argc] != NULL; argc++)
+ copy[argc] = xstrdup (argv[argc]);
+ copy[argc] = NULL;
+ return copy;
+}
+
+/*
+
+@deftypefn Extension void freeargv (char **@var{vector})
+
+Free an argument vector that was built using @code{buildargv}. Simply
+scans through @var{vector}, freeing the memory for each argument until
+the terminating @code{NULL} is found, and then frees @var{vector}
+itself.
+
+@end deftypefn
+
+*/
+
+void freeargv (char **vector)
+{
+ register char **scan;
+
+ if (vector != NULL)
+ {
+ for (scan = vector; *scan != NULL; scan++)
+ {
+ free (*scan);
+ }
+ free (vector);
+ }
+}
+
+static void
+consume_whitespace (const char **input)
+{
+ while (ISSPACE (**input))
+ {
+ (*input)++;
+ }
+}
+
+static int
+only_whitespace (const char* input)
+{
+ while (*input != EOS && ISSPACE (*input))
+ input++;
+
+ return (*input == EOS);
+}
+
+/*
+
+@deftypefn Extension char** buildargv (char *@var{sp})
+
+Given a pointer to a string, parse the string extracting fields
+separated by whitespace and optionally enclosed within either single
+or double quotes (which are stripped off), and build a vector of
+pointers to copies of the string for each field. The input string
+remains unchanged. The last element of the vector is followed by a
+@code{NULL} element.
+
+All of the memory for the pointer array and copies of the string
+is obtained from @code{xmalloc}. All of the memory can be returned to the
+system with the single function call @code{freeargv}, which takes the
+returned result of @code{buildargv}, as it's argument.
+
+Returns a pointer to the argument vector if successful. Returns
+@code{NULL} if @var{sp} is @code{NULL} or if there is insufficient
+memory to complete building the argument vector.
+
+If the input is a null string (as opposed to a @code{NULL} pointer),
+then buildarg returns an argument vector that has one arg, a null
+string.
+
+@end deftypefn
+
+The memory for the argv array is dynamically expanded as necessary.
+
+In order to provide a working buffer for extracting arguments into,
+with appropriate stripping of quotes and translation of backslash
+sequences, we allocate a working buffer at least as long as the input
+string. This ensures that we always have enough space in which to
+work, since the extracted arg is never larger than the input string.
+
+The argument vector is always kept terminated with a @code{NULL} arg
+pointer, so it can be passed to @code{freeargv} at any time, or
+returned, as appropriate.
+
+*/
+
+char **buildargv (const char *input)
+{
+ char *arg;
+ char *copybuf;
+ int squote = 0;
+ int dquote = 0;
+ int bsquote = 0;
+ int argc = 0;
+ int maxargc = 0;
+ char **argv = NULL;
+ char **nargv;
+
+ if (input != NULL)
+ {
+ copybuf = (char *) xmalloc (strlen (input) + 1);
+ /* Is a do{}while to always execute the loop once. Always return an
+ argv, even for null strings. See NOTES above, test case below. */
+ do
+ {
+ /* Pick off argv[argc] */
+ consume_whitespace (&input);
+
+ if ((maxargc == 0) || (argc >= (maxargc - 1)))
+ {
+ /* argv needs initialization, or expansion */
+ if (argv == NULL)
+ {
+ maxargc = INITIAL_MAXARGC;
+ nargv = (char **) xmalloc (maxargc * sizeof (char *));
+ }
+ else
+ {
+ maxargc *= 2;
+ nargv = (char **) xrealloc (argv, maxargc * sizeof (char *));
+ }
+ argv = nargv;
+ argv[argc] = NULL;
+ }
+ /* Begin scanning arg */
+ arg = copybuf;
+ while (*input != EOS)
+ {
+ if (ISSPACE (*input) && !squote && !dquote && !bsquote)
+ {
+ break;
+ }
+ else
+ {
+ if (bsquote)
+ {
+ bsquote = 0;
+ *arg++ = *input;
+ }
+ else if (*input == '\\')
+ {
+ bsquote = 1;
+ }
+ else if (squote)
+ {
+ if (*input == '\'')
+ {
+ squote = 0;
+ }
+ else
+ {
+ *arg++ = *input;
+ }
+ }
+ else if (dquote)
+ {
+ if (*input == '"')
+ {
+ dquote = 0;
+ }
+ else
+ {
+ *arg++ = *input;
+ }
+ }
+ else
+ {
+ if (*input == '\'')
+ {
+ squote = 1;
+ }
+ else if (*input == '"')
+ {
+ dquote = 1;
+ }
+ else
+ {
+ *arg++ = *input;
+ }
+ }
+ input++;
+ }
+ }
+ *arg = EOS;
+ argv[argc] = xstrdup (copybuf);
+ argc++;
+ argv[argc] = NULL;
+
+ consume_whitespace (&input);
+ }
+ while (*input != EOS);
+
+ free (copybuf);
+ }
+ return (argv);
+}
+
+/*
+
+@deftypefn Extension int writeargv (char * const *@var{argv}, FILE *@var{file})
+
+Write each member of ARGV, handling all necessary quoting, to the file
+named by FILE, separated by whitespace. Return 0 on success, non-zero
+if an error occurred while writing to FILE.
+
+@end deftypefn
+
+*/
+
+int
+writeargv (char * const *argv, FILE *f)
+{
+ int status = 0;
+
+ if (f == NULL)
+ return 1;
+
+ while (*argv != NULL)
+ {
+ const char *arg = *argv;
+
+ while (*arg != EOS)
+ {
+ char c = *arg;
+
+ if (ISSPACE(c) || c == '\\' || c == '\'' || c == '"')
+ if (EOF == fputc ('\\', f))
+ {
+ status = 1;
+ goto done;
+ }
+
+ if (EOF == fputc (c, f))
+ {
+ status = 1;
+ goto done;
+ }
+ arg++;
+ }
+
+ /* Write out a pair of quotes for an empty argument. */
+ if (arg == *argv)
+ if (EOF == fputs ("\"\"", f))
+ {
+ status = 1;
+ goto done;
+ }
+
+ if (EOF == fputc ('\n', f))
+ {
+ status = 1;
+ goto done;
+ }
+ argv++;
+ }
+
+ done:
+ return status;
+}
+
+/*
+
+@deftypefn Extension void expandargv (int *@var{argcp}, char ***@var{argvp})
+
+The @var{argcp} and @code{argvp} arguments are pointers to the usual
+@code{argc} and @code{argv} arguments to @code{main}. This function
+looks for arguments that begin with the character @samp{@@}. Any such
+arguments are interpreted as ``response files''. The contents of the
+response file are interpreted as additional command line options. In
+particular, the file is separated into whitespace-separated strings;
+each such string is taken as a command-line option. The new options
+are inserted in place of the option naming the response file, and
+@code{*argcp} and @code{*argvp} will be updated. If the value of
+@code{*argvp} is modified by this function, then the new value has
+been dynamically allocated and can be deallocated by the caller with
+@code{freeargv}. However, most callers will simply call
+@code{expandargv} near the beginning of @code{main} and allow the
+operating system to free the memory when the program exits.
+
+@end deftypefn
+
+*/
+
+void
+expandargv (int *argcp, char ***argvp)
+{
+ /* The argument we are currently processing. */
+ int i = 0;
+ /* To check if ***argvp has been dynamically allocated. */
+ char ** const original_argv = *argvp;
+ /* Limit the number of response files that we parse in order
+ to prevent infinite recursion. */
+ unsigned int iteration_limit = 2000;
+ /* Loop over the arguments, handling response files. We always skip
+ ARGVP[0], as that is the name of the program being run. */
+ while (++i < *argcp)
+ {
+ /* The name of the response file. */
+ const char *filename;
+ /* The response file. */
+ FILE *f;
+ /* An upper bound on the number of characters in the response
+ file. */
+ long pos;
+ /* The number of characters in the response file, when actually
+ read. */
+ size_t len;
+ /* A dynamically allocated buffer used to hold options read from a
+ response file. */
+ char *buffer;
+ /* Dynamically allocated storage for the options read from the
+ response file. */
+ char **file_argv;
+ /* The number of options read from the response file, if any. */
+ size_t file_argc;
+#ifdef S_ISDIR
+ struct stat sb;
+#endif
+ /* We are only interested in options of the form "@file". */
+ filename = (*argvp)[i];
+ if (filename[0] != '@')
+ continue;
+ /* If we have iterated too many times then stop. */
+ if (-- iteration_limit == 0)
+ {
+ fprintf (stderr, "%s: error: too many @-files encountered\n", (*argvp)[0]);
+ xexit (1);
+ }
+#ifdef S_ISDIR
+ if (stat (filename+1, &sb) < 0)
+ continue;
+ if (S_ISDIR(sb.st_mode))
+ {
+ fprintf (stderr, "%s: error: @-file refers to a directory\n", (*argvp)[0]);
+ xexit (1);
+ }
+#endif
+ /* Read the contents of the file. */
+ f = fopen (++filename, "r");
+ if (!f)
+ continue;
+ if (fseek (f, 0L, SEEK_END) == -1)
+ goto error;
+ pos = ftell (f);
+ if (pos == -1)
+ goto error;
+ if (fseek (f, 0L, SEEK_SET) == -1)
+ goto error;
+ buffer = (char *) xmalloc (pos * sizeof (char) + 1);
+ len = fread (buffer, sizeof (char), pos, f);
+ if (len != (size_t) pos
+ /* On Windows, fread may return a value smaller than POS,
+ due to CR/LF->CR translation when reading text files.
+ That does not in-and-of itself indicate failure. */
+ && ferror (f))
+ {
+ free (buffer);
+ goto error;
+ }
+ /* Add a NUL terminator. */
+ buffer[len] = '\0';
+ /* If the file is empty or contains only whitespace, buildargv would
+ return a single empty argument. In this context we want no arguments,
+ instead. */
+ if (only_whitespace (buffer))
+ {
+ file_argv = (char **) xmalloc (sizeof (char *));
+ file_argv[0] = NULL;
+ }
+ else
+ /* Parse the string. */
+ file_argv = buildargv (buffer);
+ /* If *ARGVP is not already dynamically allocated, copy it. */
+ if (*argvp == original_argv)
+ *argvp = dupargv (*argvp);
+ /* Count the number of arguments. */
+ file_argc = 0;
+ while (file_argv[file_argc])
+ ++file_argc;
+ /* Free the original option's memory. */
+ free ((*argvp)[i]);
+ /* Now, insert FILE_ARGV into ARGV. The "+1" below handles the
+ NULL terminator at the end of ARGV. */
+ *argvp = ((char **)
+ xrealloc (*argvp,
+ (*argcp + file_argc + 1) * sizeof (char *)));
+ memmove (*argvp + i + file_argc, *argvp + i + 1,
+ (*argcp - i) * sizeof (char *));
+ memcpy (*argvp + i, file_argv, file_argc * sizeof (char *));
+ /* The original option has been replaced by all the new
+ options. */
+ *argcp += file_argc - 1;
+ /* Free up memory allocated to process the response file. We do
+ not use freeargv because the individual options in FILE_ARGV
+ are now in the main ARGV. */
+ free (file_argv);
+ free (buffer);
+ /* Rescan all of the arguments just read to support response
+ files that include other response files. */
+ --i;
+ error:
+ /* We're all done with the file now. */
+ fclose (f);
+ }
+}
+
+/*
+
+@deftypefn Extension int countargv (char * const *@var{argv})
+
+Return the number of elements in @var{argv}.
+Returns zero if @var{argv} is NULL.
+
+@end deftypefn
+
+*/
+
+int
+countargv (char * const *argv)
+{
+ int argc;
+
+ if (argv == NULL)
+ return 0;
+ for (argc = 0; argv[argc] != NULL; argc++)
+ continue;
+ return argc;
+}
+
+#ifdef MAIN
+
+/* Simple little test driver. */
+
+static const char *const tests[] =
+{
+ "a simple command line",
+ "arg 'foo' is single quoted",
+ "arg \"bar\" is double quoted",
+ "arg \"foo bar\" has embedded whitespace",
+ "arg 'Jack said \\'hi\\'' has single quotes",
+ "arg 'Jack said \\\"hi\\\"' has double quotes",
+ "a b c d e f g h i j k l m n o p q r s t u v w x y z 1 2 3 4 5 6 7 8 9",
+
+ /* This should be expanded into only one argument. */
+ "trailing-whitespace ",
+
+ "",
+ NULL
+};
+
+int
+main (void)
+{
+ char **argv;
+ const char *const *test;
+ char **targs;
+
+ for (test = tests; *test != NULL; test++)
+ {
+ printf ("buildargv(\"%s\")\n", *test);
+ if ((argv = buildargv (*test)) == NULL)
+ {
+ printf ("failed!\n\n");
+ }
+ else
+ {
+ for (targs = argv; *targs != NULL; targs++)
+ {
+ printf ("\t\"%s\"\n", *targs);
+ }
+ printf ("\n");
+ }
+ freeargv (argv);
+ }
+
+ return 0;
+}
+
+#endif /* MAIN */
diff --git a/rtemstoolkit/libiberty/cp-demangle.c b/rtemstoolkit/libiberty/cp-demangle.c
index 7b8d0b4..2ce984f 100644
--- a/rtemstoolkit/libiberty/cp-demangle.c
+++ b/rtemstoolkit/libiberty/cp-demangle.c
@@ -1,5 +1,5 @@
/* Demangler for g++ V3 ABI.
- Copyright (C) 2003-2017 Free Software Foundation, Inc.
+ Copyright (C) 2003-2023 Free Software Foundation, Inc.
Written by Ian Lance Taylor <ian@wasabisystems.com>.
This file is part of the libiberty library, which is part of GCC.
@@ -30,7 +30,7 @@
/* This code implements a demangler for the g++ V3 ABI. The ABI is
described on this web page:
- http://www.codesourcery.com/cxx-abi/abi.html#mangling
+ https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
This code was written while looking at the demangler written by
Alex Samuel <samuel@codesourcery.com>.
@@ -192,9 +192,9 @@ static void d_init_info (const char *, int, size_t, struct d_info *);
#else
#ifdef __STDC__
#ifdef __STDC_VERSION__
-#if __STDC_VERSION__ >= 199901L
+#if __STDC_VERSION__ >= 199901L && !__STDC_NO_VLA__
#define CP_DYNAMIC_ARRAYS
-#endif /* __STDC__VERSION >= 199901L */
+#endif /* __STDC_VERSION__ >= 199901L && !__STDC_NO_VLA__ */
#endif /* defined (__STDC_VERSION__) */
#endif /* defined (__STDC__) */
#endif /* ! defined (__GNUC__) */
@@ -347,9 +347,9 @@ struct d_print_info
/* Number of times d_print_comp was recursively called. Should not
be bigger than MAX_RECURSION_COUNT. */
int recursion;
- /* Non-zero if we're printing a lambda argument. A template
- parameter reference actually means 'auto'. */
- int is_lambda_arg;
+ /* 1 more than the number of explicit template parms of a lambda. Template
+ parm references >= are actually 'auto'. */
+ int lambda_tpl_parms;
/* The current index into any template argument packs we are using
for printing, or -1 to print the whole pack. */
int pack_index;
@@ -425,13 +425,16 @@ is_ctor_dtor_or_conversion (struct demangle_component *);
static struct demangle_component *d_encoding (struct d_info *, int);
-static struct demangle_component *d_name (struct d_info *);
+static struct demangle_component *d_name (struct d_info *, int substable);
static struct demangle_component *d_nested_name (struct d_info *);
-static struct demangle_component *d_prefix (struct d_info *);
+static int d_maybe_module_name (struct d_info *, struct demangle_component **);
-static struct demangle_component *d_unqualified_name (struct d_info *);
+static struct demangle_component *d_prefix (struct d_info *, int);
+
+static struct demangle_component *d_unqualified_name (struct d_info *,
+ struct demangle_component *scope, struct demangle_component *module);
static struct demangle_component *d_source_name (struct d_info *);
@@ -462,7 +465,7 @@ static struct demangle_component *
d_bare_function_type (struct d_info *, int);
static struct demangle_component *
-d_class_enum_type (struct d_info *);
+d_class_enum_type (struct d_info *, int);
static struct demangle_component *d_array_type (struct d_info *);
@@ -488,6 +491,10 @@ static struct demangle_component *d_local_name (struct d_info *);
static int d_discriminator (struct d_info *);
+static struct demangle_component *d_template_parm (struct d_info *, int *bad);
+
+static struct demangle_component *d_template_head (struct d_info *, int *bad);
+
static struct demangle_component *d_lambda (struct d_info *);
static struct demangle_component *d_unnamed_type (struct d_info *);
@@ -517,7 +524,7 @@ d_growable_string_callback_adapter (const char *, size_t, void *);
static void
d_print_init (struct d_print_info *, demangle_callbackref, void *,
- const struct demangle_component *);
+ struct demangle_component *);
static inline void d_print_error (struct d_print_info *);
@@ -568,22 +575,6 @@ static int d_demangle_callback (const char *, int,
demangle_callbackref, void *);
static char *d_demangle (const char *, int, size_t *);
-/* True iff TYPE is a demangling component representing a
- function-type-qualifier. */
-
-static int
-is_fnqual_component_type (enum demangle_component_type type)
-{
- return (type == DEMANGLE_COMPONENT_RESTRICT_THIS
- || type == DEMANGLE_COMPONENT_VOLATILE_THIS
- || type == DEMANGLE_COMPONENT_CONST_THIS
- || type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS
- || type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
- || type == DEMANGLE_COMPONENT_NOEXCEPT
- || type == DEMANGLE_COMPONENT_THROW_SPEC
- || type == DEMANGLE_COMPONENT_REFERENCE_THIS);
-}
-
#define FNQUAL_COMPONENT_CASE \
case DEMANGLE_COMPONENT_RESTRICT_THIS: \
case DEMANGLE_COMPONENT_VOLATILE_THIS: \
@@ -594,6 +585,23 @@ is_fnqual_component_type (enum demangle_component_type type)
case DEMANGLE_COMPONENT_NOEXCEPT: \
case DEMANGLE_COMPONENT_THROW_SPEC
+/* True iff TYPE is a demangling component representing a
+ function-type-qualifier. */
+
+static int
+is_fnqual_component_type (enum demangle_component_type type)
+{
+ switch (type)
+ {
+ FNQUAL_COMPONENT_CASE:
+ return 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+
#ifdef CP_DEMANGLE_DEBUG
static void
@@ -624,6 +632,9 @@ d_dump (struct demangle_component *dc, int indent)
case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
printf ("template parameter %ld\n", dc->u.s_number.number);
return;
+ case DEMANGLE_COMPONENT_TPARM_OBJ:
+ printf ("template parameter object\n");
+ break;
case DEMANGLE_COMPONENT_FUNCTION_PARAM:
printf ("function parameter %ld\n", dc->u.s_number.number);
return;
@@ -641,6 +652,13 @@ d_dump (struct demangle_component *dc, int indent)
case DEMANGLE_COMPONENT_BUILTIN_TYPE:
printf ("builtin type %s\n", dc->u.s_builtin.type->name);
return;
+ case DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE:
+ {
+ char suffix[2] = { dc->u.s_extended_builtin.type->suffix, 0 };
+ printf ("builtin type %s%d%s\n", dc->u.s_extended_builtin.type->name,
+ dc->u.s_extended_builtin.type->arg, suffix);
+ }
+ return;
case DEMANGLE_COMPONENT_OPERATOR:
printf ("operator %s\n", dc->u.s_operator.op->name);
return;
@@ -764,11 +782,6 @@ d_dump (struct demangle_component *dc, int indent)
case DEMANGLE_COMPONENT_PTRMEM_TYPE:
printf ("pointer to member type\n");
break;
- case DEMANGLE_COMPONENT_FIXED_TYPE:
- printf ("fixed-point type, accum? %d, sat? %d\n",
- dc->u.s_fixed.accum, dc->u.s_fixed.sat);
- d_dump (dc->u.s_fixed.length, indent + 2);
- break;
case DEMANGLE_COMPONENT_ARGLIST:
printf ("argument list\n");
break;
@@ -811,6 +824,9 @@ d_dump (struct demangle_component *dc, int indent)
case DEMANGLE_COMPONENT_LITERAL_NEG:
printf ("negative literal\n");
break;
+ case DEMANGLE_COMPONENT_VENDOR_EXPR:
+ printf ("vendor expression\n");
+ break;
case DEMANGLE_COMPONENT_JAVA_RESOURCE:
printf ("java resource\n");
break;
@@ -857,9 +873,10 @@ CP_STATIC_IF_GLIBCPP_V3
int
cplus_demangle_fill_name (struct demangle_component *p, const char *s, int len)
{
- if (p == NULL || s == NULL || len == 0)
+ if (p == NULL || s == NULL || len <= 0)
return 0;
p->d_printing = 0;
+ p->d_counting = 0;
p->type = DEMANGLE_COMPONENT_NAME;
p->u.s_name.s = s;
p->u.s_name.len = len;
@@ -876,6 +893,7 @@ cplus_demangle_fill_extended_operator (struct demangle_component *p, int args,
if (p == NULL || args < 0 || name == NULL)
return 0;
p->d_printing = 0;
+ p->d_counting = 0;
p->type = DEMANGLE_COMPONENT_EXTENDED_OPERATOR;
p->u.s_extended_operator.args = args;
p->u.s_extended_operator.name = name;
@@ -896,6 +914,7 @@ cplus_demangle_fill_ctor (struct demangle_component *p,
|| (int) kind > gnu_v3_object_ctor_group)
return 0;
p->d_printing = 0;
+ p->d_counting = 0;
p->type = DEMANGLE_COMPONENT_CTOR;
p->u.s_ctor.kind = kind;
p->u.s_ctor.name = name;
@@ -916,6 +935,7 @@ cplus_demangle_fill_dtor (struct demangle_component *p,
|| (int) kind > gnu_v3_object_dtor_group)
return 0;
p->d_printing = 0;
+ p->d_counting = 0;
p->type = DEMANGLE_COMPONENT_DTOR;
p->u.s_dtor.kind = kind;
p->u.s_dtor.name = name;
@@ -933,6 +953,7 @@ d_make_empty (struct d_info *di)
return NULL;
p = &di->comps[di->next_comp];
p->d_printing = 0;
+ p->d_counting = 0;
++di->next_comp;
return p;
}
@@ -967,9 +988,11 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
case DEMANGLE_COMPONENT_TRINARY_ARG1:
case DEMANGLE_COMPONENT_LITERAL:
case DEMANGLE_COMPONENT_LITERAL_NEG:
+ case DEMANGLE_COMPONENT_VENDOR_EXPR:
case DEMANGLE_COMPONENT_COMPOUND_NAME:
case DEMANGLE_COMPONENT_VECTOR_TYPE:
case DEMANGLE_COMPONENT_CLONE:
+ case DEMANGLE_COMPONENT_MODULE_ENTITY:
if (left == NULL || right == NULL)
return NULL;
break;
@@ -1006,6 +1029,14 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
case DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS:
case DEMANGLE_COMPONENT_NULLARY:
case DEMANGLE_COMPONENT_TRINARY_ARG2:
+ case DEMANGLE_COMPONENT_TPARM_OBJ:
+ case DEMANGLE_COMPONENT_STRUCTURED_BINDING:
+ case DEMANGLE_COMPONENT_MODULE_INIT:
+ case DEMANGLE_COMPONENT_TEMPLATE_HEAD:
+ case DEMANGLE_COMPONENT_TEMPLATE_NON_TYPE_PARM:
+ case DEMANGLE_COMPONENT_TEMPLATE_TEMPLATE_PARM:
+ case DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM:
+ case DEMANGLE_COMPONENT_FRIEND:
if (left == NULL)
return NULL;
break;
@@ -1014,6 +1045,8 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
empty. */
case DEMANGLE_COMPONENT_ARRAY_TYPE:
case DEMANGLE_COMPONENT_INITIALIZER_LIST:
+ case DEMANGLE_COMPONENT_MODULE_NAME:
+ case DEMANGLE_COMPONENT_MODULE_PARTITION:
if (right == NULL)
return NULL;
break;
@@ -1026,6 +1059,7 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
case DEMANGLE_COMPONENT_CONST:
case DEMANGLE_COMPONENT_ARGLIST:
case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST:
+ case DEMANGLE_COMPONENT_TEMPLATE_TYPE_PARM:
FNQUAL_COMPONENT_CASE:
break;
@@ -1087,6 +1121,28 @@ d_make_builtin_type (struct d_info *di,
return p;
}
+/* Add a new extended builtin type component. */
+
+static struct demangle_component *
+d_make_extended_builtin_type (struct d_info *di,
+ const struct demangle_builtin_type_info *type,
+ short arg, char suffix)
+{
+ struct demangle_component *p;
+
+ if (type == NULL)
+ return NULL;
+ p = d_make_empty (di);
+ if (p != NULL)
+ {
+ p->type = DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE;
+ p->u.s_extended_builtin.type = type;
+ p->u.s_extended_builtin.arg = arg;
+ p->u.s_extended_builtin.suffix = suffix;
+ }
+ return p;
+}
+
/* Add a new operator component. */
static struct demangle_component *
@@ -1258,6 +1314,8 @@ has_return_type (struct demangle_component *dc)
{
default:
return 0;
+ case DEMANGLE_COMPONENT_LOCAL_NAME:
+ return has_return_type (d_right (dc));
case DEMANGLE_COMPONENT_TEMPLATE:
return ! is_ctor_dtor_or_conversion (d_left (dc));
FNQUAL_COMPONENT_CASE:
@@ -1300,25 +1358,22 @@ static struct demangle_component *
d_encoding (struct d_info *di, int top_level)
{
char peek = d_peek_char (di);
+ struct demangle_component *dc;
if (peek == 'G' || peek == 'T')
- return d_special_name (di);
+ dc = d_special_name (di);
else
{
- struct demangle_component *dc;
+ dc = d_name (di, 0);
- dc = d_name (di);
-
- if (dc != NULL && top_level && (di->options & DMGL_PARAMS) == 0)
+ if (!dc)
+ /* Failed already. */;
+ else if (top_level && (di->options & DMGL_PARAMS) == 0)
{
/* Strip off any initial CV-qualifiers, as they really apply
to the `this' parameter, and they were not output by the
v2 demangler without DMGL_PARAMS. */
- while (dc->type == DEMANGLE_COMPONENT_RESTRICT_THIS
- || dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS
- || dc->type == DEMANGLE_COMPONENT_CONST_THIS
- || dc->type == DEMANGLE_COMPONENT_REFERENCE_THIS
- || dc->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS)
+ while (is_fnqual_component_type (dc->type))
dc = d_left (dc);
/* If the top level is a DEMANGLE_COMPONENT_LOCAL_NAME, then
@@ -1327,23 +1382,42 @@ d_encoding (struct d_info *di, int top_level)
which is local to a function. */
if (dc->type == DEMANGLE_COMPONENT_LOCAL_NAME)
{
- struct demangle_component *dcr;
+ while (d_right (dc) != NULL
+ && is_fnqual_component_type (d_right (dc)->type))
+ d_right (dc) = d_left (d_right (dc));
- dcr = d_right (dc);
- while (is_fnqual_component_type (dcr->type))
- dcr = d_left (dcr);
- dc->u.s_binary.right = dcr;
+ if (d_right (dc) == NULL)
+ dc = NULL;
}
-
- return dc;
}
+ else
+ {
+ peek = d_peek_char (di);
+ if (peek != '\0' && peek != 'E')
+ {
+ struct demangle_component *ftype;
- peek = d_peek_char (di);
- if (dc == NULL || peek == '\0' || peek == 'E')
- return dc;
- return d_make_comp (di, DEMANGLE_COMPONENT_TYPED_NAME, dc,
- d_bare_function_type (di, has_return_type (dc)));
+ ftype = d_bare_function_type (di, has_return_type (dc));
+ if (ftype)
+ {
+ /* If this is a non-top-level local-name, clear the
+ return type, so it doesn't confuse the user by
+ being confused with the return type of whaever
+ this is nested within. */
+ if (!top_level && dc->type == DEMANGLE_COMPONENT_LOCAL_NAME
+ && ftype->type == DEMANGLE_COMPONENT_FUNCTION_TYPE)
+ d_left (ftype) = NULL;
+
+ dc = d_make_comp (di, DEMANGLE_COMPONENT_TYPED_NAME,
+ dc, ftype);
+ }
+ else
+ dc = NULL;
+ }
+ }
}
+
+ return dc;
}
/* <tagged-name> ::= <name> B <source-name> */
@@ -1384,80 +1458,74 @@ d_abi_tags (struct d_info *di, struct demangle_component *dc)
*/
static struct demangle_component *
-d_name (struct d_info *di)
+d_name (struct d_info *di, int substable)
{
char peek = d_peek_char (di);
- struct demangle_component *dc;
+ struct demangle_component *dc = NULL;
+ struct demangle_component *module = NULL;
+ int subst = 0;
switch (peek)
{
case 'N':
- return d_nested_name (di);
+ dc = d_nested_name (di);
+ break;
case 'Z':
- return d_local_name (di);
+ dc = d_local_name (di);
+ break;
case 'U':
- return d_unqualified_name (di);
+ dc = d_unqualified_name (di, NULL, NULL);
+ break;
case 'S':
{
- int subst;
-
- if (d_peek_next_char (di) != 't')
- {
- dc = d_substitution (di, 0);
- subst = 1;
- }
- else
+ if (d_peek_next_char (di) == 't')
{
d_advance (di, 2);
- dc = d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME,
- d_make_name (di, "std", 3),
- d_unqualified_name (di));
+ dc = d_make_name (di, "std", 3);
di->expansion += 3;
- subst = 0;
}
- if (d_peek_char (di) != 'I')
+ if (d_peek_char (di) == 'S')
{
- /* The grammar does not permit this case to occur if we
- called d_substitution() above (i.e., subst == 1). We
- don't bother to check. */
- }
- else
- {
- /* This is <template-args>, which means that we just saw
- <unscoped-template-name>, which is a substitution
- candidate if we didn't just get it from a
- substitution. */
- if (! subst)
+ module = d_substitution (di, 0);
+ if (!module)
+ return NULL;
+ if (!(module->type == DEMANGLE_COMPONENT_MODULE_NAME
+ || module->type == DEMANGLE_COMPONENT_MODULE_PARTITION))
{
- if (! d_add_substitution (di, dc))
+ if (dc)
return NULL;
+ subst = 1;
+ dc = module;
+ module = NULL;
}
- dc = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, dc,
- d_template_args (di));
}
-
- return dc;
}
+ /* FALLTHROUGH */
case 'L':
default:
- dc = d_unqualified_name (di);
+ if (!subst)
+ dc = d_unqualified_name (di, dc, module);
if (d_peek_char (di) == 'I')
{
/* This is <template-args>, which means that we just saw
<unscoped-template-name>, which is a substitution
candidate. */
- if (! d_add_substitution (di, dc))
+ if (!subst && !d_add_substitution (di, dc))
return NULL;
dc = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, dc,
d_template_args (di));
+ subst = 0;
}
- return dc;
+ break;
}
+ if (substable && !subst && !d_add_substitution (di, dc))
+ return NULL;
+ return dc;
}
/* <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E
@@ -1482,7 +1550,7 @@ d_nested_name (struct d_info *di)
once we have something to attach it to. */
rqual = d_ref_qualifier (di, NULL);
- *pret = d_prefix (di);
+ *pret = d_prefix (di, 1);
if (*pret == NULL)
return NULL;
@@ -1508,105 +1576,149 @@ d_nested_name (struct d_info *di)
<template-prefix> ::= <prefix> <(template) unqualified-name>
::= <template-param>
::= <substitution>
-*/
+
+ SUBST is true if we should add substitutions (as normal), false
+ if not (in an unresolved-name). */
static struct demangle_component *
-d_prefix (struct d_info *di)
+d_prefix (struct d_info *di, int substable)
{
struct demangle_component *ret = NULL;
- while (1)
+ for (;;)
{
- char peek;
- enum demangle_component_type comb_type;
- struct demangle_component *dc;
-
- peek = d_peek_char (di);
- if (peek == '\0')
- return NULL;
+ char peek = d_peek_char (di);
/* The older code accepts a <local-name> here, but I don't see
that in the grammar. The older code does not accept a
<template-param> here. */
- comb_type = DEMANGLE_COMPONENT_QUAL_NAME;
- if (peek == 'D')
+ if (peek == 'D'
+ && (d_peek_next_char (di) == 'T'
+ || d_peek_next_char (di) == 't'))
{
- char peek2 = d_peek_next_char (di);
- if (peek2 == 'T' || peek2 == 't')
- /* Decltype. */
- dc = cplus_demangle_type (di);
- else
- /* Destructor name. */
- dc = d_unqualified_name (di);
+ /* Decltype. */
+ if (ret)
+ return NULL;
+ ret = cplus_demangle_type (di);
}
- else if (IS_DIGIT (peek)
- || IS_LOWER (peek)
- || peek == 'C'
- || peek == 'U'
- || peek == 'L')
- dc = d_unqualified_name (di);
- else if (peek == 'S')
- dc = d_substitution (di, 1);
else if (peek == 'I')
{
if (ret == NULL)
return NULL;
- comb_type = DEMANGLE_COMPONENT_TEMPLATE;
- dc = d_template_args (di);
+ struct demangle_component *dc = d_template_args (di);
+ if (!dc)
+ return NULL;
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret, dc);
}
else if (peek == 'T')
- dc = d_template_param (di);
- else if (peek == 'E')
- return ret;
- else if (peek == 'M')
{
- /* Initializer scope for a lambda. We don't need to represent
- this; the normal code will just treat the variable as a type
- scope, which gives appropriate output. */
- if (ret == NULL)
+ if (ret)
return NULL;
+ ret = d_template_param (di);
+ }
+ else if (peek == 'M')
+ {
+ /* Initializer scope for a lambda. We already added it as a
+ substitution candidate, don't do that again. */
d_advance (di, 1);
continue;
}
else
+ {
+ struct demangle_component *module = NULL;
+ if (peek == 'S')
+ {
+ module = d_substitution (di, 1);
+ if (!module)
+ return NULL;
+ if (!(module->type == DEMANGLE_COMPONENT_MODULE_NAME
+ || module->type == DEMANGLE_COMPONENT_MODULE_PARTITION))
+ {
+ if (ret)
+ return NULL;
+ ret = module;
+ continue;
+ }
+ }
+ ret = d_unqualified_name (di, ret, module);
+ }
+
+ if (!ret)
+ break;
+
+ if (d_peek_char (di) == 'E')
+ break;
+
+ if (substable && !d_add_substitution (di, ret))
return NULL;
+ }
- if (ret == NULL)
- ret = dc;
- else
- ret = d_make_comp (di, comb_type, ret, dc);
+ return ret;
+}
- if (peek != 'S' && d_peek_char (di) != 'E')
+static int
+d_maybe_module_name (struct d_info *di, struct demangle_component **name)
+{
+ while (d_peek_char (di) == 'W')
+ {
+ d_advance (di, 1);
+ enum demangle_component_type code = DEMANGLE_COMPONENT_MODULE_NAME;
+ if (d_peek_char (di) == 'P')
{
- if (! d_add_substitution (di, ret))
- return NULL;
+ code = DEMANGLE_COMPONENT_MODULE_PARTITION;
+ d_advance (di, 1);
}
+
+ *name = d_make_comp (di, code, *name, d_source_name (di));
+ if (!*name)
+ return 0;
+ if (!d_add_substitution (di, *name))
+ return 0;
}
+ return 1;
}
-/* <unqualified-name> ::= <operator-name>
- ::= <ctor-dtor-name>
- ::= <source-name>
- ::= <local-source-name>
-
- <local-source-name> ::= L <source-name> <discriminator>
+/* <unqualified-name> ::= [<module-name>] <operator-name> [<abi-tags>]
+ ::= [<module-name>] <ctor-dtor-name> [<abi-tags>]
+ ::= [<module-name>] <source-name> [<abi-tags>]
+ ::= [<module-name>] F <source-name> [<abi-tags>]
+ ::= [<module-name>] <local-source-name> [<abi-tags>]
+ ::= [<module-name>] DC <source-name>+ E [<abi-tags>]
+ <local-source-name> ::= L <source-name> <discriminator> [<abi-tags>]
*/
static struct demangle_component *
-d_unqualified_name (struct d_info *di)
+d_unqualified_name (struct d_info *di, struct demangle_component *scope,
+ struct demangle_component *module)
{
struct demangle_component *ret;
char peek;
+ int member_like_friend = 0;
+
+ if (!d_maybe_module_name (di, &module))
+ return NULL;
peek = d_peek_char (di);
+ if (peek == 'F')
+ {
+ member_like_friend = 1;
+ d_advance (di, 1);
+ peek = d_peek_char (di);
+ }
if (IS_DIGIT (peek))
ret = d_source_name (di);
else if (IS_LOWER (peek))
{
+ int was_expr = di->is_expression;
if (peek == 'o' && d_peek_next_char (di) == 'n')
- d_advance (di, 2);
+ {
+ d_advance (di, 2);
+ /* Treat cv as naming a conversion operator. */
+ di->is_expression = 0;
+ }
ret = d_operator_name (di);
+ di->is_expression = was_expr;
if (ret != NULL && ret->type == DEMANGLE_COMPONENT_OPERATOR)
{
di->expansion += sizeof "operator" + ret->u.s_operator.op->len - 2;
@@ -1615,6 +1727,28 @@ d_unqualified_name (struct d_info *di)
d_source_name (di));
}
}
+ else if (peek == 'D' && d_peek_next_char (di) == 'C')
+ {
+ // structured binding
+ d_advance (di, 2);
+ struct demangle_component *prev = NULL;
+ do
+ {
+ struct demangle_component *next =
+ d_make_comp (di, DEMANGLE_COMPONENT_STRUCTURED_BINDING,
+ d_source_name (di), NULL);
+ if (prev)
+ d_right (prev) = next;
+ else
+ ret = next;
+ prev = next;
+ }
+ while (prev && d_peek_char (di) != 'E');
+ if (prev)
+ d_advance (di, 1);
+ else
+ ret = NULL;
+ }
else if (peek == 'C' || peek == 'D')
ret = d_ctor_dtor_name (di);
else if (peek == 'L')
@@ -1644,8 +1778,15 @@ d_unqualified_name (struct d_info *di)
else
return NULL;
+ if (module)
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_MODULE_ENTITY, ret, module);
if (d_peek_char (di) == 'B')
ret = d_abi_tags (di, ret);
+ if (member_like_friend)
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_FRIEND, ret, NULL);
+ if (scope)
+ ret = d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, scope, ret);
+
return ret;
}
@@ -1694,7 +1835,7 @@ d_number (struct d_info *di)
}
if (ret > ((INT_MAX - (peek - '0')) / 10))
return -1;
- ret = ret * 10 + peek - '0';
+ ret = ret * 10 + (peek - '0');
d_advance (di, 1);
peek = d_peek_char (di);
}
@@ -1774,19 +1915,23 @@ const struct demangle_operator_info cplus_demangle_operators[] =
{ "ad", NL ("&"), 1 },
{ "an", NL ("&"), 2 },
{ "at", NL ("alignof "), 1 },
+ { "aw", NL ("co_await "), 1 },
{ "az", NL ("alignof "), 1 },
{ "cc", NL ("const_cast"), 2 },
{ "cl", NL ("()"), 2 },
{ "cm", NL (","), 2 },
{ "co", NL ("~"), 1 },
{ "dV", NL ("/="), 2 },
+ { "dX", NL ("[...]="), 3 }, /* [expr...expr] = expr */
{ "da", NL ("delete[] "), 1 },
{ "dc", NL ("dynamic_cast"), 2 },
{ "de", NL ("*"), 1 },
+ { "di", NL ("="), 2 }, /* .name = expr */
{ "dl", NL ("delete "), 1 },
{ "ds", NL (".*"), 2 },
{ "dt", NL ("."), 2 },
{ "dv", NL ("/"), 2 },
+ { "dx", NL ("]="), 2 }, /* [expr] = expr */
{ "eO", NL ("^="), 2 },
{ "eo", NL ("^"), 2 },
{ "eq", NL ("=="), 2 },
@@ -1813,6 +1958,7 @@ const struct demangle_operator_info cplus_demangle_operators[] =
{ "ng", NL ("-"), 1 },
{ "nt", NL ("!"), 1 },
{ "nw", NL ("new"), 3 },
+ { "nx", NL ("noexcept"), 1 },
{ "oR", NL ("|="), 2 },
{ "oo", NL ("||"), 2 },
{ "or", NL ("|"), 2 },
@@ -1831,6 +1977,7 @@ const struct demangle_operator_info cplus_demangle_operators[] =
{ "sP", NL ("sizeof..."), 1 },
{ "sZ", NL ("sizeof..."), 1 },
{ "sc", NL ("static_cast"), 2 },
+ { "ss", NL ("<=>"), 2 },
{ "st", NL ("sizeof "), 1 },
{ "sz", NL ("sizeof "), 1 },
{ "tr", NL ("throw"), 0 },
@@ -1994,6 +2141,7 @@ d_java_resource (struct d_info *di)
::= TT <type>
::= TI <type>
::= TS <type>
+ ::= TA <template-arg>
::= GV <(object) name>
::= T <call-offset> <(base) encoding>
::= Tc <call-offset> <call-offset> <(base) encoding>
@@ -2080,11 +2228,15 @@ d_special_name (struct d_info *di)
case 'H':
return d_make_comp (di, DEMANGLE_COMPONENT_TLS_INIT,
- d_name (di), NULL);
+ d_name (di, 0), NULL);
case 'W':
return d_make_comp (di, DEMANGLE_COMPONENT_TLS_WRAPPER,
- d_name (di), NULL);
+ d_name (di, 0), NULL);
+
+ case 'A':
+ return d_make_comp (di, DEMANGLE_COMPONENT_TPARM_OBJ,
+ d_template_arg (di), NULL);
default:
return NULL;
@@ -2095,11 +2247,12 @@ d_special_name (struct d_info *di)
switch (d_next_char (di))
{
case 'V':
- return d_make_comp (di, DEMANGLE_COMPONENT_GUARD, d_name (di), NULL);
+ return d_make_comp (di, DEMANGLE_COMPONENT_GUARD,
+ d_name (di, 0), NULL);
case 'R':
{
- struct demangle_component *name = d_name (di);
+ struct demangle_component *name = d_name (di, 0);
return d_make_comp (di, DEMANGLE_COMPONENT_REFTEMP, name,
d_number_component (di));
}
@@ -2108,6 +2261,14 @@ d_special_name (struct d_info *di)
return d_make_comp (di, DEMANGLE_COMPONENT_HIDDEN_ALIAS,
d_encoding (di, 0), NULL);
+ case 'I':
+ {
+ struct demangle_component *module = NULL;
+ if (!d_maybe_module_name (di, &module) || !module)
+ return NULL;
+ return d_make_comp (di, DEMANGLE_COMPONENT_MODULE_INIT,
+ module, NULL);
+ }
case 'T':
switch (d_next_char (di))
{
@@ -2341,10 +2502,13 @@ cplus_demangle_builtin_types[D_BUILTIN_TYPE_COUNT] =
/* 27 */ { NL ("decimal64"), NL ("decimal64"), D_PRINT_DEFAULT },
/* 28 */ { NL ("decimal128"), NL ("decimal128"), D_PRINT_DEFAULT },
/* 29 */ { NL ("half"), NL ("half"), D_PRINT_FLOAT },
- /* 30 */ { NL ("char16_t"), NL ("char16_t"), D_PRINT_DEFAULT },
- /* 31 */ { NL ("char32_t"), NL ("char32_t"), D_PRINT_DEFAULT },
- /* 32 */ { NL ("decltype(nullptr)"), NL ("decltype(nullptr)"),
+ /* 30 */ { NL ("char8_t"), NL ("char8_t"), D_PRINT_DEFAULT },
+ /* 31 */ { NL ("char16_t"), NL ("char16_t"), D_PRINT_DEFAULT },
+ /* 32 */ { NL ("char32_t"), NL ("char32_t"), D_PRINT_DEFAULT },
+ /* 33 */ { NL ("decltype(nullptr)"), NL ("decltype(nullptr)"),
D_PRINT_DEFAULT },
+ /* 34 */ { NL ("_Float"), NL ("_Float"), D_PRINT_FLOAT },
+ /* 35 */ { NL ("std::bfloat16_t"), NL ("std::bfloat16_t"), D_PRINT_FLOAT },
};
CP_STATIC_IF_GLIBCPP_V3
@@ -2429,13 +2593,6 @@ cplus_demangle_type (struct d_info *di)
ret = d_function_type (di);
break;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- case 'N':
- case 'Z':
- ret = d_class_enum_type (di);
- break;
-
case 'A':
ret = d_array_type (di);
break;
@@ -2506,39 +2663,6 @@ cplus_demangle_type (struct d_info *di)
}
break;
- case 'S':
- /* If this is a special substitution, then it is the start of
- <class-enum-type>. */
- {
- char peek_next;
-
- peek_next = d_peek_next_char (di);
- if (IS_DIGIT (peek_next)
- || peek_next == '_'
- || IS_UPPER (peek_next))
- {
- ret = d_substitution (di, 0);
- /* The substituted name may have been a template name and
- may be followed by tepmlate args. */
- if (d_peek_char (di) == 'I')
- ret = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, ret,
- d_template_args (di));
- else
- can_subst = 0;
- }
- else
- {
- ret = d_class_enum_type (di);
- /* If the substitution was a complete type, then it is not
- a new substitution candidate. However, if the
- substitution was followed by template arguments, then
- the whole thing is a substitution candidate. */
- if (ret != NULL && ret->type == DEMANGLE_COMPONENT_SUB_STD)
- can_subst = 0;
- }
- }
- break;
-
case 'O':
d_advance (di, 1);
ret = d_make_comp (di, DEMANGLE_COMPONENT_RVALUE_REFERENCE,
@@ -2631,31 +2755,54 @@ cplus_demangle_type (struct d_info *di)
ret = d_make_builtin_type (di, &cplus_demangle_builtin_types[29]);
di->expansion += ret->u.s_builtin.type->len;
break;
+ case 'u':
+ /* char8_t */
+ ret = d_make_builtin_type (di, &cplus_demangle_builtin_types[30]);
+ di->expansion += ret->u.s_builtin.type->len;
+ break;
case 's':
/* char16_t */
- ret = d_make_builtin_type (di, &cplus_demangle_builtin_types[30]);
+ ret = d_make_builtin_type (di, &cplus_demangle_builtin_types[31]);
di->expansion += ret->u.s_builtin.type->len;
break;
case 'i':
/* char32_t */
- ret = d_make_builtin_type (di, &cplus_demangle_builtin_types[31]);
+ ret = d_make_builtin_type (di, &cplus_demangle_builtin_types[32]);
di->expansion += ret->u.s_builtin.type->len;
break;
case 'F':
- /* Fixed point types. DF<int bits><length><fract bits><sat> */
- ret = d_make_empty (di);
- ret->type = DEMANGLE_COMPONENT_FIXED_TYPE;
- if ((ret->u.s_fixed.accum = IS_DIGIT (d_peek_char (di))))
- /* For demangling we don't care about the bits. */
- d_number (di);
- ret->u.s_fixed.length = cplus_demangle_type (di);
- if (ret->u.s_fixed.length == NULL)
- return NULL;
- d_number (di);
- peek = d_next_char (di);
- ret->u.s_fixed.sat = (peek == 's');
- break;
+ /* DF<number>_ - _Float<number>.
+ DF<number>x - _Float<number>x
+ DF16b - std::bfloat16_t. */
+ {
+ int arg = d_number (di);
+ char buf[12];
+ char suffix = 0;
+ if (d_peek_char (di) == 'b')
+ {
+ if (arg != 16)
+ return NULL;
+ d_advance (di, 1);
+ ret = d_make_builtin_type (di,
+ &cplus_demangle_builtin_types[35]);
+ di->expansion += ret->u.s_builtin.type->len;
+ break;
+ }
+ if (d_peek_char (di) == 'x')
+ suffix = 'x';
+ if (!suffix && d_peek_char (di) != '_')
+ return NULL;
+ ret
+ = d_make_extended_builtin_type (di,
+ &cplus_demangle_builtin_types[34],
+ arg, suffix);
+ d_advance (di, 1);
+ sprintf (buf, "%d", arg);
+ di->expansion += ret->u.s_extended_builtin.type->len
+ + strlen (buf) + (suffix != 0);
+ break;
+ }
case 'v':
ret = d_vector_type (di);
@@ -2664,7 +2811,7 @@ cplus_demangle_type (struct d_info *di)
case 'n':
/* decltype(nullptr) */
- ret = d_make_builtin_type (di, &cplus_demangle_builtin_types[32]);
+ ret = d_make_builtin_type (di, &cplus_demangle_builtin_types[33]);
di->expansion += ret->u.s_builtin.type->len;
break;
@@ -2674,7 +2821,7 @@ cplus_demangle_type (struct d_info *di)
break;
default:
- return NULL;
+ return d_class_enum_type (di, 1);
}
if (can_subst)
@@ -2829,21 +2976,35 @@ d_ref_qualifier (struct d_info *di, struct demangle_component *sub)
static struct demangle_component *
d_function_type (struct d_info *di)
{
- struct demangle_component *ret;
+ struct demangle_component *ret = NULL;
- if (! d_check_char (di, 'F'))
- return NULL;
- if (d_peek_char (di) == 'Y')
+ if ((di->options & DMGL_NO_RECURSE_LIMIT) == 0)
{
- /* Function has C linkage. We don't print this information.
- FIXME: We should print it in verbose mode. */
- d_advance (di, 1);
+ if (di->recursion_level > DEMANGLE_RECURSION_LIMIT)
+ /* FIXME: There ought to be a way to report
+ that the recursion limit has been reached. */
+ return NULL;
+
+ di->recursion_level ++;
}
- ret = d_bare_function_type (di, 1);
- ret = d_ref_qualifier (di, ret);
- if (! d_check_char (di, 'E'))
- return NULL;
+ if (d_check_char (di, 'F'))
+ {
+ if (d_peek_char (di) == 'Y')
+ {
+ /* Function has C linkage. We don't print this information.
+ FIXME: We should print it in verbose mode. */
+ d_advance (di, 1);
+ }
+ ret = d_bare_function_type (di, 1);
+ ret = d_ref_qualifier (di, ret);
+
+ if (! d_check_char (di, 'E'))
+ ret = NULL;
+ }
+
+ if ((di->options & DMGL_NO_RECURSE_LIMIT) == 0)
+ di->recursion_level --;
return ret;
}
@@ -2933,9 +3094,9 @@ d_bare_function_type (struct d_info *di, int has_return_type)
/* <class-enum-type> ::= <name> */
static struct demangle_component *
-d_class_enum_type (struct d_info *di)
+d_class_enum_type (struct d_info *di, int substable)
{
- return d_name (di);
+ return d_name (di, substable);
}
/* <array-type> ::= A <(positive dimension) number> _ <(element) type>
@@ -3226,18 +3387,69 @@ op_is_new_cast (struct demangle_component *op)
|| code[0] == 'c' || code[0] == 'r'));
}
+/* <unresolved-name> ::= [gs] <base-unresolved-name> # x or (with "gs") ::x
+ ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x
+ # T::N::x /decltype(p)::N::x
+ ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
+ # A::x, N::y, A<T>::z; "gs" means leading "::"
+ ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>
+
+ "gs" is handled elsewhere, as a unary operator. */
+
+static struct demangle_component *
+d_unresolved_name (struct d_info *di)
+{
+ struct demangle_component *type;
+ struct demangle_component *name;
+ char peek;
+
+ /* Consume the "sr". */
+ d_advance (di, 2);
+
+ peek = d_peek_char (di);
+ if (di->unresolved_name_state
+ && (IS_DIGIT (peek)
+ || IS_LOWER (peek)
+ || peek == 'C'
+ || peek == 'U'
+ || peek == 'L'))
+ {
+ /* The third production is ambiguous with the old unresolved-name syntax
+ of <type> <base-unresolved-name>; in the old mangling, A::x was mangled
+ as sr1A1x, now sr1AE1x. So we first try to demangle using the new
+ mangling, then with the old if that fails. */
+ di->unresolved_name_state = -1;
+ type = d_prefix (di, 0);
+ if (d_peek_char (di) == 'E')
+ d_advance (di, 1);
+ }
+ else
+ type = cplus_demangle_type (di);
+ name = d_unqualified_name (di, type, NULL);
+ if (d_peek_char (di) == 'I')
+ name = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, name,
+ d_template_args (di));
+ return name;
+}
+
/* <expression> ::= <(unary) operator-name> <expression>
::= <(binary) operator-name> <expression> <expression>
::= <(trinary) operator-name> <expression> <expression> <expression>
::= cl <expression>+ E
::= st <type>
::= <template-param>
- ::= sr <type> <unqualified-name>
- ::= sr <type> <unqualified-name> <template-args>
+ ::= u <source-name> <template-arg>* E # vendor extended expression
+ ::= <unresolved-name>
::= <expr-primary>
+
+ <braced-expression> ::= <expression>
+ ::= di <field source-name> <braced-expression> # .name = expr
+ ::= dx <index expression> <braced-expression> # [expr] = expr
+ ::= dX <range begin expression> <range end expression> <braced-expression>
+ # [expr ... expr] = expr
*/
-static inline struct demangle_component *
+static struct demangle_component *
d_expression_1 (struct d_info *di)
{
char peek;
@@ -3248,20 +3460,7 @@ d_expression_1 (struct d_info *di)
else if (peek == 'T')
return d_template_param (di);
else if (peek == 's' && d_peek_next_char (di) == 'r')
- {
- struct demangle_component *type;
- struct demangle_component *name;
-
- d_advance (di, 2);
- type = cplus_demangle_type (di);
- name = d_unqualified_name (di);
- if (d_peek_char (di) != 'I')
- return d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, type, name);
- else
- return d_make_comp (di, DEMANGLE_COMPONENT_QUAL_NAME, type,
- d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE, name,
- d_template_args (di)));
- }
+ return d_unresolved_name (di);
else if (peek == 's' && d_peek_next_char (di) == 'p')
{
d_advance (di, 2);
@@ -3299,7 +3498,7 @@ d_expression_1 (struct d_info *di)
/* operator-function-id, i.e. operator+(t). */
d_advance (di, 2);
- name = d_unqualified_name (di);
+ name = d_unqualified_name (di, NULL, NULL);
if (name == NULL)
return NULL;
if (d_peek_char (di) == 'I')
@@ -3313,14 +3512,23 @@ d_expression_1 (struct d_info *di)
{
/* Brace-enclosed initializer list, untyped or typed. */
struct demangle_component *type = NULL;
+ d_advance (di, 2);
if (peek == 't')
type = cplus_demangle_type (di);
- if (!d_peek_next_char (di))
+ if (!d_peek_char (di) || !d_peek_next_char (di))
return NULL;
- d_advance (di, 2);
return d_make_comp (di, DEMANGLE_COMPONENT_INITIALIZER_LIST,
type, d_exprlist (di, 'E'));
}
+ else if (peek == 'u')
+ {
+ /* A vendor extended expression. */
+ struct demangle_component *name, *args;
+ d_advance (di, 1);
+ name = d_source_name (di);
+ args = d_template_args_1 (di);
+ return d_make_comp (di, DEMANGLE_COMPONENT_VENDOR_EXPR, name, args);
+ }
else
{
struct demangle_component *op;
@@ -3380,13 +3588,10 @@ d_expression_1 (struct d_info *di)
if (suffix)
/* Indicate the suffix variant for d_print_comp. */
- return d_make_comp (di, DEMANGLE_COMPONENT_UNARY, op,
- d_make_comp (di,
- DEMANGLE_COMPONENT_BINARY_ARGS,
- operand, operand));
- else
- return d_make_comp (di, DEMANGLE_COMPONENT_UNARY, op,
- operand);
+ operand = d_make_comp (di, DEMANGLE_COMPONENT_BINARY_ARGS,
+ operand, operand);
+
+ return d_make_comp (di, DEMANGLE_COMPONENT_UNARY, op, operand);
}
case 2:
{
@@ -3400,16 +3605,30 @@ d_expression_1 (struct d_info *di)
else if (code[0] == 'f')
/* fold-expression. */
left = d_operator_name (di);
+ else if (!strcmp (code, "di"))
+ left = d_unqualified_name (di, NULL, NULL);
else
left = d_expression_1 (di);
if (!strcmp (code, "cl"))
right = d_exprlist (di, 'E');
else if (!strcmp (code, "dt") || !strcmp (code, "pt"))
{
- right = d_unqualified_name (di);
- if (d_peek_char (di) == 'I')
- right = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE,
- right, d_template_args (di));
+ peek = d_peek_char (di);
+ /* These codes start a qualified name. */
+ if ((peek == 'g' && d_peek_next_char (di) == 's')
+ || (peek == 's' && d_peek_next_char (di) == 'r'))
+ right = d_expression_1 (di);
+ else
+ {
+ /* Otherwise it's an unqualified name. We use
+ d_unqualified_name rather than d_expression_1 here for
+ old mangled names that didn't add 'on' before operator
+ names. */
+ right = d_unqualified_name (di, NULL, NULL);
+ if (d_peek_char (di) == 'I')
+ right = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE,
+ right, d_template_args (di));
+ }
}
else
right = d_expression_1 (di);
@@ -3427,7 +3646,8 @@ d_expression_1 (struct d_info *di)
if (code == NULL)
return NULL;
- else if (!strcmp (code, "qu"))
+ else if (!strcmp (code, "qu")
+ || !strcmp (code, "dX"))
{
/* ?: expression. */
first = d_expression_1 (di);
@@ -3531,6 +3751,17 @@ d_expr_primary (struct d_info *di)
&& type->u.s_builtin.type->print != D_PRINT_DEFAULT)
di->expansion -= type->u.s_builtin.type->len;
+ if (type->type == DEMANGLE_COMPONENT_BUILTIN_TYPE
+ && strcmp (type->u.s_builtin.type->name,
+ cplus_demangle_builtin_types[33].name) == 0)
+ {
+ if (d_peek_char (di) == 'E')
+ {
+ d_advance (di, 1);
+ return type;
+ }
+ }
+
/* Rather than try to interpret the literal value, we just
collect it as a string. Note that it's possible to have a
floating point literal here. The ABI specifies that the
@@ -3571,11 +3802,14 @@ static struct demangle_component *
d_local_name (struct d_info *di)
{
struct demangle_component *function;
+ struct demangle_component *name;
if (! d_check_char (di, 'Z'))
return NULL;
function = d_encoding (di, 0);
+ if (!function)
+ return NULL;
if (! d_check_char (di, 'E'))
return NULL;
@@ -3585,13 +3819,10 @@ d_local_name (struct d_info *di)
d_advance (di, 1);
if (! d_discriminator (di))
return NULL;
- return d_make_comp (di, DEMANGLE_COMPONENT_LOCAL_NAME, function,
- d_make_name (di, "string literal",
- sizeof "string literal" - 1));
+ name = d_make_name (di, "string literal", sizeof "string literal" - 1);
}
else
{
- struct demangle_component *name;
int num = -1;
if (d_peek_char (di) == 'd')
@@ -3603,22 +3834,31 @@ d_local_name (struct d_info *di)
return NULL;
}
- name = d_name (di);
- if (name)
- switch (name->type)
- {
- /* Lambdas and unnamed types have internal discriminators. */
- case DEMANGLE_COMPONENT_LAMBDA:
- case DEMANGLE_COMPONENT_UNNAMED_TYPE:
- break;
- default:
- if (! d_discriminator (di))
- return NULL;
- }
+ name = d_name (di, 0);
+
+ if (name
+ /* Lambdas and unnamed types have internal discriminators
+ and are not functions. */
+ && name->type != DEMANGLE_COMPONENT_LAMBDA
+ && name->type != DEMANGLE_COMPONENT_UNNAMED_TYPE)
+ {
+ /* Read and ignore an optional discriminator. */
+ if (! d_discriminator (di))
+ return NULL;
+ }
+
if (num >= 0)
name = d_make_default_arg (di, num, name);
- return d_make_comp (di, DEMANGLE_COMPONENT_LOCAL_NAME, function, name);
}
+
+ /* Elide the return type of the containing function so as to not
+ confuse the user thinking it is the return type of whatever local
+ function we might be containing. */
+ if (function->type == DEMANGLE_COMPONENT_TYPED_NAME
+ && d_right (function)->type == DEMANGLE_COMPONENT_FUNCTION_TYPE)
+ d_left (d_right (function)) = NULL;
+
+ return d_make_comp (di, DEMANGLE_COMPONENT_LOCAL_NAME, function, name);
}
/* <discriminator> ::= _ <number> # when number < 10
@@ -3658,32 +3898,120 @@ d_discriminator (struct d_info *di)
return 1;
}
-/* <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ */
+/* <template-parm> ::= Ty
+ ::= Tn <type>
+ ::= Tt <template-head> E
+ ::= Tp <template-parm> */
static struct demangle_component *
-d_lambda (struct d_info *di)
+d_template_parm (struct d_info *di, int *bad)
{
- struct demangle_component *tl;
- struct demangle_component *ret;
- int num;
+ if (d_peek_char (di) != 'T')
+ return NULL;
+
+ struct demangle_component *op;
+ enum demangle_component_type kind;
+ switch (d_peek_next_char (di))
+ {
+ default:
+ return NULL;
+
+ case 'p': /* Pack */
+ d_advance (di, 2);
+ op = d_template_parm (di, bad);
+ kind = DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM;
+ if (!op)
+ {
+ *bad = 1;
+ return NULL;
+ }
+ break;
+
+ case 'y': /* Typename */
+ d_advance (di, 2);
+ op = NULL;
+ kind = DEMANGLE_COMPONENT_TEMPLATE_TYPE_PARM;
+ break;
+
+ case 'n': /* Non-Type */
+ d_advance (di, 2);
+ op = cplus_demangle_type (di);
+ kind = DEMANGLE_COMPONENT_TEMPLATE_NON_TYPE_PARM;
+ if (!op)
+ {
+ *bad = 1;
+ return NULL;
+ }
+ break;
+ case 't': /* Template */
+ d_advance (di, 2);
+ op = d_template_head (di, bad);
+ kind = DEMANGLE_COMPONENT_TEMPLATE_TEMPLATE_PARM;
+ if (!op || !d_check_char (di, 'E'))
+ {
+ *bad = 1;
+ return NULL;
+ }
+ }
+
+ return d_make_comp (di, kind, op, NULL);
+}
+
+/* <template-head> ::= <template-head>? <template-parm> */
+
+static struct demangle_component *
+d_template_head (struct d_info *di, int *bad)
+{
+ struct demangle_component *res = NULL, **slot = &res;
+ struct demangle_component *op;
+
+ while ((op = d_template_parm (di, bad)))
+ {
+ *slot = op;
+ slot = &d_right (op);
+ }
+
+ /* Wrap it in a template head, to make concatenating with any parm list, and
+ printing simpler. */
+ if (res)
+ res = d_make_comp (di, DEMANGLE_COMPONENT_TEMPLATE_HEAD, res, NULL);
+
+ return res;
+}
+
+/* <closure-type-name> ::= Ul <template-head>? <lambda-sig> E [ <nonnegative number> ] _ */
+
+static struct demangle_component *
+d_lambda (struct d_info *di)
+{
if (! d_check_char (di, 'U'))
return NULL;
if (! d_check_char (di, 'l'))
return NULL;
- tl = d_parmlist (di);
+ int bad = 0;
+ struct demangle_component *head = d_template_head (di, &bad);
+ if (bad)
+ return NULL;
+
+ struct demangle_component *tl = d_parmlist (di);
if (tl == NULL)
return NULL;
+ if (head)
+ {
+ d_right (head) = tl;
+ tl = head;
+ }
if (! d_check_char (di, 'E'))
return NULL;
- num = d_compact_number (di);
+ int num = d_compact_number (di);
if (num < 0)
return NULL;
- ret = d_make_empty (di);
+ struct demangle_component *ret = d_make_empty (di);
if (ret)
{
ret->type = DEMANGLE_COMPONENT_LAMBDA;
@@ -3691,9 +4019,6 @@ d_lambda (struct d_info *di)
ret->u.s_unary_num.num = num;
}
- if (! d_add_substitution (di, ret))
- return NULL;
-
return ret;
}
@@ -3737,10 +4062,11 @@ d_clone_suffix (struct d_info *di, struct demangle_component *encoding)
const char *pend = suffix;
struct demangle_component *n;
- if (*pend == '.' && (IS_LOWER (pend[1]) || pend[1] == '_'))
+ if (*pend == '.' && (IS_LOWER (pend[1]) || IS_DIGIT (pend[1])
+ || pend[1] == '_'))
{
pend += 2;
- while (IS_LOWER (*pend) || *pend == '_')
+ while (IS_LOWER (*pend) || IS_DIGIT (*pend) || *pend == '_')
++pend;
}
while (*pend == '.' && IS_DIGIT (pend[1]))
@@ -4012,12 +4338,14 @@ d_growable_string_callback_adapter (const char *s, size_t l, void *opaque)
are larger than the actual numbers encountered. */
static void
-d_count_templates_scopes (int *num_templates, int *num_scopes,
- const struct demangle_component *dc)
+d_count_templates_scopes (struct d_print_info *dpi,
+ struct demangle_component *dc)
{
- if (dc == NULL)
+ if (dc == NULL || dc->d_counting > 1 || dpi->recursion > MAX_RECURSION_COUNT)
return;
+ ++ dc->d_counting;
+
switch (dc->type)
{
case DEMANGLE_COMPONENT_NAME:
@@ -4025,20 +4353,31 @@ d_count_templates_scopes (int *num_templates, int *num_scopes,
case DEMANGLE_COMPONENT_FUNCTION_PARAM:
case DEMANGLE_COMPONENT_SUB_STD:
case DEMANGLE_COMPONENT_BUILTIN_TYPE:
+ case DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE:
case DEMANGLE_COMPONENT_OPERATOR:
case DEMANGLE_COMPONENT_CHARACTER:
case DEMANGLE_COMPONENT_NUMBER:
case DEMANGLE_COMPONENT_UNNAMED_TYPE:
+ case DEMANGLE_COMPONENT_STRUCTURED_BINDING:
+ case DEMANGLE_COMPONENT_MODULE_NAME:
+ case DEMANGLE_COMPONENT_MODULE_PARTITION:
+ case DEMANGLE_COMPONENT_MODULE_INIT:
+ case DEMANGLE_COMPONENT_FIXED_TYPE:
+ case DEMANGLE_COMPONENT_TEMPLATE_HEAD:
+ case DEMANGLE_COMPONENT_TEMPLATE_TYPE_PARM:
+ case DEMANGLE_COMPONENT_TEMPLATE_NON_TYPE_PARM:
+ case DEMANGLE_COMPONENT_TEMPLATE_TEMPLATE_PARM:
+ case DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM:
break;
case DEMANGLE_COMPONENT_TEMPLATE:
- (*num_templates)++;
+ dpi->num_copy_templates++;
goto recurse_left_right;
case DEMANGLE_COMPONENT_REFERENCE:
case DEMANGLE_COMPONENT_RVALUE_REFERENCE:
if (d_left (dc)->type == DEMANGLE_COMPONENT_TEMPLATE_PARAM)
- (*num_scopes)++;
+ dpi->num_saved_scopes++;
goto recurse_left_right;
case DEMANGLE_COMPONENT_QUAL_NAME:
@@ -4081,6 +4420,7 @@ d_count_templates_scopes (int *num_templates, int *num_scopes,
case DEMANGLE_COMPONENT_VECTOR_TYPE:
case DEMANGLE_COMPONENT_ARGLIST:
case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST:
+ case DEMANGLE_COMPONENT_TPARM_OBJ:
case DEMANGLE_COMPONENT_INITIALIZER_LIST:
case DEMANGLE_COMPONENT_CAST:
case DEMANGLE_COMPONENT_CONVERSION:
@@ -4093,6 +4433,7 @@ d_count_templates_scopes (int *num_templates, int *num_scopes,
case DEMANGLE_COMPONENT_TRINARY_ARG2:
case DEMANGLE_COMPONENT_LITERAL:
case DEMANGLE_COMPONENT_LITERAL_NEG:
+ case DEMANGLE_COMPONENT_VENDOR_EXPR:
case DEMANGLE_COMPONENT_JAVA_RESOURCE:
case DEMANGLE_COMPONENT_COMPOUND_NAME:
case DEMANGLE_COMPONENT_DECLTYPE:
@@ -4102,42 +4443,40 @@ d_count_templates_scopes (int *num_templates, int *num_scopes,
case DEMANGLE_COMPONENT_TAGGED_NAME:
case DEMANGLE_COMPONENT_CLONE:
recurse_left_right:
- d_count_templates_scopes (num_templates, num_scopes,
- d_left (dc));
- d_count_templates_scopes (num_templates, num_scopes,
- d_right (dc));
+ /* PR 89394 - Check for too much recursion. */
+ if (dpi->recursion > DEMANGLE_RECURSION_LIMIT)
+ /* FIXME: There ought to be a way to report to the
+ user that the recursion limit has been reached. */
+ return;
+
+ ++ dpi->recursion;
+ d_count_templates_scopes (dpi, d_left (dc));
+ d_count_templates_scopes (dpi, d_right (dc));
+ -- dpi->recursion;
break;
case DEMANGLE_COMPONENT_CTOR:
- d_count_templates_scopes (num_templates, num_scopes,
- dc->u.s_ctor.name);
+ d_count_templates_scopes (dpi, dc->u.s_ctor.name);
break;
case DEMANGLE_COMPONENT_DTOR:
- d_count_templates_scopes (num_templates, num_scopes,
- dc->u.s_dtor.name);
+ d_count_templates_scopes (dpi, dc->u.s_dtor.name);
break;
case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
- d_count_templates_scopes (num_templates, num_scopes,
- dc->u.s_extended_operator.name);
- break;
-
- case DEMANGLE_COMPONENT_FIXED_TYPE:
- d_count_templates_scopes (num_templates, num_scopes,
- dc->u.s_fixed.length);
+ d_count_templates_scopes (dpi, dc->u.s_extended_operator.name);
break;
case DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS:
case DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS:
- d_count_templates_scopes (num_templates, num_scopes,
- d_left (dc));
+ case DEMANGLE_COMPONENT_MODULE_ENTITY:
+ case DEMANGLE_COMPONENT_FRIEND:
+ d_count_templates_scopes (dpi, d_left (dc));
break;
case DEMANGLE_COMPONENT_LAMBDA:
case DEMANGLE_COMPONENT_DEFAULT_ARG:
- d_count_templates_scopes (num_templates, num_scopes,
- dc->u.s_unary_num.sub);
+ d_count_templates_scopes (dpi, dc->u.s_unary_num.sub);
break;
}
}
@@ -4146,7 +4485,7 @@ d_count_templates_scopes (int *num_templates, int *num_scopes,
static void
d_print_init (struct d_print_info *dpi, demangle_callbackref callback,
- void *opaque, const struct demangle_component *dc)
+ void *opaque, struct demangle_component *dc)
{
dpi->len = 0;
dpi->last_char = '\0';
@@ -4160,7 +4499,7 @@ d_print_init (struct d_print_info *dpi, demangle_callbackref callback,
dpi->demangle_failure = 0;
dpi->recursion = 0;
- dpi->is_lambda_arg = 0;
+ dpi->lambda_tpl_parms = 0;
dpi->component_stack = NULL;
@@ -4172,8 +4511,12 @@ d_print_init (struct d_print_info *dpi, demangle_callbackref callback,
dpi->next_copy_template = 0;
dpi->num_copy_templates = 0;
- d_count_templates_scopes (&dpi->num_copy_templates,
- &dpi->num_saved_scopes, dc);
+ d_count_templates_scopes (dpi, dc);
+ /* If we did not reach the recursion limit, then reset the
+ current recursion value back to 0, so that we can print
+ the templates. */
+ if (dpi->recursion < DEMANGLE_RECURSION_LIMIT)
+ dpi->recursion = 0;
dpi->num_copy_templates *= dpi->num_saved_scopes;
dpi->current_template = NULL;
@@ -4392,11 +4735,11 @@ d_find_pack (struct d_print_info *dpi,
case DEMANGLE_COMPONENT_TAGGED_NAME:
case DEMANGLE_COMPONENT_OPERATOR:
case DEMANGLE_COMPONENT_BUILTIN_TYPE:
+ case DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE:
case DEMANGLE_COMPONENT_SUB_STD:
case DEMANGLE_COMPONENT_CHARACTER:
case DEMANGLE_COMPONENT_FUNCTION_PARAM:
case DEMANGLE_COMPONENT_UNNAMED_TYPE:
- case DEMANGLE_COMPONENT_FIXED_TYPE:
case DEMANGLE_COMPONENT_DEFAULT_ARG:
case DEMANGLE_COMPONENT_NUMBER:
return NULL;
@@ -4595,6 +4938,91 @@ d_maybe_print_fold_expression (struct d_print_info *dpi, int options,
return 1;
}
+/* True iff DC represents a C99-style designated initializer. */
+
+static int
+is_designated_init (struct demangle_component *dc)
+{
+ if (dc->type != DEMANGLE_COMPONENT_BINARY
+ && dc->type != DEMANGLE_COMPONENT_TRINARY)
+ return 0;
+
+ struct demangle_component *op = d_left (dc);
+ const char *code = op->u.s_operator.op->code;
+ return (code[0] == 'd'
+ && (code[1] == 'i' || code[1] == 'x' || code[1] == 'X'));
+}
+
+/* If DC represents a C99-style designated initializer, print it and return
+ true; otherwise, return false. */
+
+static int
+d_maybe_print_designated_init (struct d_print_info *dpi, int options,
+ struct demangle_component *dc)
+{
+ if (!is_designated_init (dc))
+ return 0;
+
+ const char *code = d_left (dc)->u.s_operator.op->code;
+
+ struct demangle_component *operands = d_right (dc);
+ struct demangle_component *op1 = d_left (operands);
+ struct demangle_component *op2 = d_right (operands);
+
+ if (code[1] == 'i')
+ d_append_char (dpi, '.');
+ else
+ d_append_char (dpi, '[');
+
+ d_print_comp (dpi, options, op1);
+ if (code[1] == 'X')
+ {
+ d_append_string (dpi, " ... ");
+ d_print_comp (dpi, options, d_left (op2));
+ op2 = d_right (op2);
+ }
+ if (code[1] != 'i')
+ d_append_char (dpi, ']');
+ if (is_designated_init (op2))
+ {
+ /* Don't put '=' or '(' between chained designators. */
+ d_print_comp (dpi, options, op2);
+ }
+ else
+ {
+ d_append_char (dpi, '=');
+ d_print_subexpr (dpi, options, op2);
+ }
+ return 1;
+}
+
+static void
+d_print_lambda_parm_name (struct d_print_info *dpi, int type, unsigned index)
+{
+ const char *str;
+ switch (type)
+ {
+ default:
+ dpi->demangle_failure = 1;
+ str = "";
+ break;
+
+ case DEMANGLE_COMPONENT_TEMPLATE_TYPE_PARM:
+ str = "$T";
+ break;
+
+ case DEMANGLE_COMPONENT_TEMPLATE_NON_TYPE_PARM:
+ str = "$N";
+ break;
+
+ case DEMANGLE_COMPONENT_TEMPLATE_TEMPLATE_PARM:
+ str = "$TT";
+ break;
+ }
+ d_append_string (dpi, str);
+ d_append_num (dpi, index);
+}
+
/* Subroutine to handle components. */
static void
@@ -4636,6 +5064,38 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
d_append_char (dpi, ']');
return;
+ case DEMANGLE_COMPONENT_STRUCTURED_BINDING:
+ d_append_char (dpi, '[');
+ for (;;)
+ {
+ d_print_comp (dpi, options, d_left (dc));
+ dc = d_right (dc);
+ if (!dc)
+ break;
+ d_append_string (dpi, ", ");
+ }
+ d_append_char (dpi, ']');
+ return;
+
+ case DEMANGLE_COMPONENT_MODULE_ENTITY:
+ d_print_comp (dpi, options, d_left (dc));
+ d_append_char (dpi, '@');
+ d_print_comp (dpi, options, d_right (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_MODULE_NAME:
+ case DEMANGLE_COMPONENT_MODULE_PARTITION:
+ {
+ if (d_left (dc))
+ d_print_comp (dpi, options, d_left (dc));
+ char c = dc->type == DEMANGLE_COMPONENT_MODULE_PARTITION
+ ? ':' : d_left (dc) ? '.' : 0;
+ if (c)
+ d_append_char (dpi, c);
+ d_print_comp (dpi, options, d_right (dc));
+ }
+ return;
+
case DEMANGLE_COMPONENT_QUAL_NAME:
case DEMANGLE_COMPONENT_LOCAL_NAME:
d_print_comp (dpi, options, d_left (dc));
@@ -4698,32 +5158,17 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
return;
}
- /* If typed_name is a template, then it applies to the
- function type as well. */
- if (typed_name->type == DEMANGLE_COMPONENT_TEMPLATE)
- {
- dpt.next = dpi->templates;
- dpi->templates = &dpt;
- dpt.template_decl = typed_name;
- }
-
/* If typed_name is a DEMANGLE_COMPONENT_LOCAL_NAME, then
there may be CV-qualifiers on its right argument which
- really apply here; this happens when parsing a class which
+ really apply here; this happens when parsing a class that
is local to a function. */
if (typed_name->type == DEMANGLE_COMPONENT_LOCAL_NAME)
{
- struct demangle_component *local_name;
-
- local_name = d_right (typed_name);
- if (local_name->type == DEMANGLE_COMPONENT_DEFAULT_ARG)
- local_name = local_name->u.s_unary_num.sub;
- if (local_name == NULL)
- {
- d_print_error (dpi);
- return;
- }
- while (is_fnqual_component_type (local_name->type))
+ typed_name = d_right (typed_name);
+ if (typed_name->type == DEMANGLE_COMPONENT_DEFAULT_ARG)
+ typed_name = typed_name->u.s_unary_num.sub;
+ while (typed_name != NULL
+ && is_fnqual_component_type (typed_name->type))
{
if (i >= sizeof adpm / sizeof adpm[0])
{
@@ -4735,15 +5180,29 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
adpm[i].next = &adpm[i - 1];
dpi->modifiers = &adpm[i];
- adpm[i - 1].mod = local_name;
+ adpm[i - 1].mod = typed_name;
adpm[i - 1].printed = 0;
adpm[i - 1].templates = dpi->templates;
++i;
- local_name = d_left (local_name);
+ typed_name = d_left (typed_name);
+ }
+ if (typed_name == NULL)
+ {
+ d_print_error (dpi);
+ return;
}
}
+ /* If typed_name is a template, then it applies to the
+ function type as well. */
+ if (typed_name->type == DEMANGLE_COMPONENT_TEMPLATE)
+ {
+ dpt.next = dpi->templates;
+ dpi->templates = &dpt;
+ dpt.template_decl = typed_name;
+ }
+
d_print_comp (dpi, options, d_right (dc));
if (typed_name->type == DEMANGLE_COMPONENT_TEMPLATE)
@@ -4818,7 +5277,21 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
}
case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
- if (dpi->is_lambda_arg)
+ if (dpi->lambda_tpl_parms > dc->u.s_number.number + 1)
+ {
+ const struct demangle_component *a
+ = d_left (dpi->templates->template_decl);
+ unsigned c;
+ for (c = dc->u.s_number.number; a && c; c--)
+ a = d_right (a);
+ if (a && a->type == DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM)
+ a = d_left (a);
+ if (!a)
+ dpi->demangle_failure = 1;
+ else
+ d_print_lambda_parm_name (dpi, a->type, dc->u.s_number.number);
+ }
+ else if (dpi->lambda_tpl_parms)
{
/* Show the template parm index, as that's how g++ displays
these, and future proofs us against potential
@@ -4854,6 +5327,11 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
}
return;
+ case DEMANGLE_COMPONENT_TPARM_OBJ:
+ d_append_string (dpi, "template parameter object for ");
+ d_print_comp (dpi, options, d_left (dc));
+ return;
+
case DEMANGLE_COMPONENT_CTOR:
d_print_comp (dpi, options, dc->u.s_ctor.name);
return;
@@ -4863,6 +5341,11 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
d_print_comp (dpi, options, dc->u.s_dtor.name);
return;
+ case DEMANGLE_COMPONENT_MODULE_INIT:
+ d_append_string (dpi, "initializer for module ");
+ d_print_comp (dpi, options, d_left (dc));
+ return;
+
case DEMANGLE_COMPONENT_VTABLE:
d_append_string (dpi, "vtable for ");
d_print_comp (dpi, options, d_left (dc));
@@ -4989,7 +5472,7 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
{
/* Handle reference smashing: & + && = &. */
struct demangle_component *sub = d_left (dc);
- if (!dpi->is_lambda_arg
+ if (!dpi->lambda_tpl_parms
&& sub->type == DEMANGLE_COMPONENT_TEMPLATE_PARAM)
{
struct d_saved_scope *scope = d_get_saved_scope (dpi, sub);
@@ -5100,6 +5583,14 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
dc->u.s_builtin.type->java_len);
return;
+ case DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE:
+ d_append_buffer (dpi, dc->u.s_extended_builtin.type->name,
+ dc->u.s_extended_builtin.type->len);
+ d_append_num (dpi, dc->u.s_extended_builtin.arg);
+ if (dc->u.s_extended_builtin.suffix)
+ d_append_buffer (dpi, &dc->u.s_extended_builtin.suffix, 1);
+ return;
+
case DEMANGLE_COMPONENT_VENDOR_TYPE:
d_print_comp (dpi, options, d_left (dc));
return;
@@ -5238,22 +5729,6 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
return;
}
- case DEMANGLE_COMPONENT_FIXED_TYPE:
- if (dc->u.s_fixed.sat)
- d_append_string (dpi, "_Sat ");
- /* Don't print "int _Accum". */
- if (dc->u.s_fixed.length->u.s_builtin.type
- != &cplus_demangle_builtin_types['i'-'a'])
- {
- d_print_comp (dpi, options, dc->u.s_fixed.length);
- d_append_char (dpi, ' ');
- }
- if (dc->u.s_fixed.accum)
- d_append_string (dpi, "_Accum");
- else
- d_append_string (dpi, "_Fract");
- return;
-
case DEMANGLE_COMPONENT_ARGLIST:
case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST:
if (d_left (dc) != NULL)
@@ -5374,8 +5849,8 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
if (code && !strcmp (code, "gs"))
/* Avoid parens after '::'. */
d_print_comp (dpi, options, operand);
- else if (code && !strcmp (code, "st"))
- /* Always print parens for sizeof (type). */
+ else if (code && (!strcmp (code, "st") || !strcmp (code, "nx")))
+ /* Always print parens for sizeof (type) and noexcept(expr). */
{
d_append_char (dpi, '(');
d_print_comp (dpi, options, operand);
@@ -5407,6 +5882,9 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
if (d_maybe_print_fold_expression (dpi, options, dc))
return;
+ if (d_maybe_print_designated_init (dpi, options, dc))
+ return;
+
/* We wrap an expression which uses the greater-than operator in
an extra layer of parens so that it does not get confused
with the '>' which ends the template parameters. */
@@ -5464,6 +5942,8 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
}
if (d_maybe_print_fold_expression (dpi, options, dc))
return;
+ if (d_maybe_print_designated_init (dpi, options, dc))
+ return;
{
struct demangle_component *op = d_left (dc);
struct demangle_component *first = d_left (d_right (dc));
@@ -5583,6 +6063,13 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
}
return;
+ case DEMANGLE_COMPONENT_VENDOR_EXPR:
+ d_print_comp (dpi, options, d_left (dc));
+ d_append_char (dpi, '(');
+ d_print_comp (dpi, options, d_right (dc));
+ d_append_char (dpi, ')');
+ return;
+
case DEMANGLE_COMPONENT_NUMBER:
d_append_num (dpi, dc->u.s_number.number);
return;
@@ -5609,9 +6096,10 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
case DEMANGLE_COMPONENT_PACK_EXPANSION:
{
- int len;
- int i;
- struct demangle_component *a = d_find_pack (dpi, d_left (dc));
+ struct demangle_component *a = NULL;
+
+ if (!dpi->lambda_tpl_parms)
+ a = d_find_pack (dpi, d_left (dc));
if (a == NULL)
{
/* d_find_pack won't find anything if the only packs involved
@@ -5619,17 +6107,20 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
case, just print the pattern and "...". */
d_print_subexpr (dpi, options, d_left (dc));
d_append_string (dpi, "...");
- return;
}
-
- len = d_pack_length (a);
- dc = d_left (dc);
- for (i = 0; i < len; ++i)
+ else
{
- dpi->pack_index = i;
- d_print_comp (dpi, options, dc);
- if (i < len-1)
- d_append_string (dpi, ", ");
+ int len = d_pack_length (a);
+ int i;
+
+ dc = d_left (dc);
+ for (i = 0; i < len; ++i)
+ {
+ if (i)
+ d_append_string (dpi, ", ");
+ dpi->pack_index = i;
+ d_print_comp (dpi, options, dc);
+ }
}
}
return;
@@ -5659,15 +6150,50 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
return;
case DEMANGLE_COMPONENT_LAMBDA:
- d_append_string (dpi, "{lambda(");
- /* Generic lambda auto parms are mangled as the template type
- parm they are. */
- dpi->is_lambda_arg++;
- d_print_comp (dpi, options, dc->u.s_unary_num.sub);
- dpi->is_lambda_arg--;
- d_append_string (dpi, ")#");
- d_append_num (dpi, dc->u.s_unary_num.num + 1);
- d_append_char (dpi, '}');
+ {
+ d_append_string (dpi, "{lambda");
+ struct demangle_component *parms = dc->u.s_unary_num.sub;
+ struct d_print_template dpt;
+ /* Generic lambda auto parms are mangled as the (synthedic) template
+ type parm they are. We need to tell the printer that (a) we're in
+ a lambda, and (b) the number of synthetic parms. */
+ int saved_tpl_parms = dpi->lambda_tpl_parms;
+ dpi->lambda_tpl_parms = 0;
+ /* Hang any lambda head as-if template args. */
+ dpt.template_decl = NULL;
+ dpt.next = dpi->templates;
+ dpi->templates = &dpt;
+ if (parms && parms->type == DEMANGLE_COMPONENT_TEMPLATE_HEAD)
+ {
+ dpt.template_decl = parms;
+
+ d_append_char (dpi, '<');
+ struct demangle_component *parm;
+ for (parm = d_left (parms); parm; parm = d_right (parm))
+ {
+ if (dpi->lambda_tpl_parms++)
+ d_append_string (dpi, ", ");
+ d_print_comp (dpi, options, parm);
+ d_append_char (dpi, ' ');
+ if (parm->type == DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM)
+ parm = d_left (parm);
+ d_print_lambda_parm_name (dpi, parm->type,
+ dpi->lambda_tpl_parms - 1);
+ }
+ d_append_char (dpi, '>');
+
+ parms = d_right (parms);
+ }
+ dpi->lambda_tpl_parms++;
+
+ d_append_char (dpi, '(');
+ d_print_comp (dpi, options, parms);
+ dpi->lambda_tpl_parms = saved_tpl_parms;
+ dpi->templates = dpt.next;
+ d_append_string (dpi, ")#");
+ d_append_num (dpi, dc->u.s_unary_num.num + 1);
+ d_append_char (dpi, '}');
+ }
return;
case DEMANGLE_COMPONENT_UNNAMED_TYPE:
@@ -5683,6 +6209,45 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
d_append_char (dpi, ']');
return;
+ case DEMANGLE_COMPONENT_FRIEND:
+ d_print_comp (dpi, options, d_left (dc));
+ d_append_string (dpi, "[friend]");
+ return;
+
+ case DEMANGLE_COMPONENT_TEMPLATE_HEAD:
+ {
+ d_append_char (dpi, '<');
+ int count = 0;
+ struct demangle_component *parm;
+ for (parm = d_left (dc); parm; parm = d_right (parm))
+ {
+ if (count++)
+ d_append_string (dpi, ", ");
+ d_print_comp (dpi, options, parm);
+ }
+ d_append_char (dpi, '>');
+ }
+ return;
+
+ case DEMANGLE_COMPONENT_TEMPLATE_TYPE_PARM:
+ d_append_string (dpi, "typename");
+ return;
+
+ case DEMANGLE_COMPONENT_TEMPLATE_NON_TYPE_PARM:
+ d_print_comp (dpi, options, d_left (dc));
+ return;
+
+ case DEMANGLE_COMPONENT_TEMPLATE_TEMPLATE_PARM:
+ d_append_string (dpi, "template");
+ d_print_comp (dpi, options, d_left (dc));
+ d_append_string (dpi, " class");
+ return;
+
+ case DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM:
+ d_print_comp (dpi, options, d_left (dc));
+ d_append_string (dpi, "...");
+ return;
+
default:
d_print_error (dpi);
return;
@@ -5913,10 +6478,10 @@ d_print_mod (struct d_print_info *dpi, int options,
d_append_string (dpi, "&&");
return;
case DEMANGLE_COMPONENT_COMPLEX:
- d_append_string (dpi, "complex ");
+ d_append_string (dpi, " _Complex");
return;
case DEMANGLE_COMPONENT_IMAGINARY:
- d_append_string (dpi, "imaginary ");
+ d_append_string (dpi, " _Imaginary");
return;
case DEMANGLE_COMPONENT_PTRMEM_TYPE:
if (d_last_char (dpi) != '(')
@@ -6112,32 +6677,10 @@ d_print_conversion (struct d_print_info *dpi, int options,
dpt.template_decl = dpi->current_template;
}
- if (d_left (dc)->type != DEMANGLE_COMPONENT_TEMPLATE)
- {
- d_print_comp (dpi, options, d_left (dc));
- if (dpi->current_template != NULL)
- dpi->templates = dpt.next;
- }
- else
- {
- d_print_comp (dpi, options, d_left (d_left (dc)));
-
- /* For a templated cast operator, we need to remove the template
- parameters from scope after printing the operator name,
- so we need to handle the template printing here. */
- if (dpi->current_template != NULL)
- dpi->templates = dpt.next;
+ d_print_comp (dpi, options, d_left (dc));
- if (d_last_char (dpi) == '<')
- d_append_char (dpi, ' ');
- d_append_char (dpi, '<');
- d_print_comp (dpi, options, d_right (d_left (dc)));
- /* Avoid generating two consecutive '>' characters, to avoid
- the C++ syntactic ambiguity. */
- if (d_last_char (dpi) == '>')
- d_append_char (dpi, ' ');
- d_append_char (dpi, '>');
- }
+ if (dpi->current_template != NULL)
+ dpi->templates = dpt.next;
}
/* Initialize the information structure we use to pass around
@@ -6154,13 +6697,13 @@ cplus_demangle_init_info (const char *mangled, int options, size_t len,
di->n = mangled;
- /* We can not need more components than twice the number of chars in
+ /* We cannot need more components than twice the number of chars in
the mangled string. Most components correspond directly to
chars, but the ARGLIST types are exceptions. */
di->num_comps = 2 * len;
di->next_comp = 0;
- /* Similarly, we can not need more substitutions than there are
+ /* Similarly, we cannot need more substitutions than there are
chars in the mangled string. */
di->num_subs = len;
di->next_sub = 0;
@@ -6170,6 +6713,7 @@ cplus_demangle_init_info (const char *mangled, int options, size_t len,
di->expansion = 0;
di->is_expression = 0;
di->is_conversion = 0;
+ di->recursion_level = 0;
}
/* Internal implementation for the demangler. If MANGLED is a g++ v3 ABI
@@ -6207,8 +6751,25 @@ d_demangle_callback (const char *mangled, int options,
type = DCT_TYPE;
}
+ di.unresolved_name_state = 1;
+
+ again:
cplus_demangle_init_info (mangled, options, strlen (mangled), &di);
+ /* PR 87675 - Check for a mangled string that is so long
+ that we do not have enough stack space to demangle it. */
+ if (((options & DMGL_NO_RECURSE_LIMIT) == 0)
+ /* This check is a bit arbitrary, since what we really want to do is to
+ compare the sizes of the di.comps and di.subs arrays against the
+ amount of stack space remaining. But there is no portable way to do
+ this, so instead we use the recursion limit as a guide to the maximum
+ size of the arrays. */
+ && (unsigned long) di.num_comps > DEMANGLE_RECURSION_LIMIT)
+ {
+ /* FIXME: We need a way to indicate that a stack limit has been reached. */
+ return 0;
+ }
+
{
#ifdef CP_DYNAMIC_ARRAYS
__extension__ struct demangle_component comps[di.num_comps];
@@ -6251,6 +6812,13 @@ d_demangle_callback (const char *mangled, int options,
if (((options & DMGL_PARAMS) != 0) && d_peek_char (&di) != '\0')
dc = NULL;
+ /* See discussion in d_unresolved_name. */
+ if (dc == NULL && di.unresolved_name_state == -1)
+ {
+ di.unresolved_name_state = 0;
+ goto again;
+ }
+
#ifdef CP_DEMANGLE_DEBUG
d_dump (dc, 0);
#endif
diff --git a/rtemstoolkit/libiberty/cp-demangle.h b/rtemstoolkit/libiberty/cp-demangle.h
index d4a4ab6..8563f91 100644
--- a/rtemstoolkit/libiberty/cp-demangle.h
+++ b/rtemstoolkit/libiberty/cp-demangle.h
@@ -1,5 +1,5 @@
/* Internal demangler interface for g++ V3 ABI.
- Copyright (C) 2003-2017 Free Software Foundation, Inc.
+ Copyright (C) 2003-2023 Free Software Foundation, Inc.
Written by Ian Lance Taylor <ian@wasabisystems.com>.
This file is part of the libiberty library, which is part of GCC.
@@ -122,6 +122,13 @@ struct d_info
/* Non-zero if we are parsing the type operand of a conversion
operator, but not when in an expression. */
int is_conversion;
+ /* 1: using new unresolved-name grammar.
+ -1: using new unresolved-name grammar and saw an unresolved-name.
+ 0: using old unresolved-name grammar. */
+ int unresolved_name_state;
+ /* If DMGL_NO_RECURSE_LIMIT is not active then this is set to
+ the current recursion level. */
+ unsigned int recursion_level;
};
/* To avoid running past the ending '\0', don't:
@@ -173,7 +180,7 @@ d_advance (struct d_info *di, int i)
extern const struct demangle_operator_info cplus_demangle_operators[];
#endif
-#define D_BUILTIN_TYPE_COUNT (33)
+#define D_BUILTIN_TYPE_COUNT (36)
CP_STATIC_IF_GLIBCPP_V3
const struct demangle_builtin_type_info
diff --git a/rtemstoolkit/libiberty/cplus-dem.c b/rtemstoolkit/libiberty/cplus-dem.c
index 81c17a3..9d499d6 100644
--- a/rtemstoolkit/libiberty/cplus-dem.c
+++ b/rtemstoolkit/libiberty/cplus-dem.c
@@ -1,5 +1,5 @@
/* Demangler for GNU C++
- Copyright (C) 1989-2017 Free Software Foundation, Inc.
+ Copyright (C) 1989-2023 Free Software Foundation, Inc.
Written by James Clark (jjc@jclark.uucp)
Rewritten by Fred Fish (fnf@cygnus.com) for ARM and Lucid demangling
Modified by Satish Pai (pai@apollo.hp.com) for HP demangling
@@ -29,12 +29,6 @@ License along with libiberty; see the file COPYING.LIB. If
not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
Boston, MA 02110-1301, USA. */
-/* This file exports two functions; cplus_mangle_opname and cplus_demangle.
-
- This file imports xmalloc and xrealloc, which are like malloc and
- realloc except that they generate a fatal error if there is no
- available memory. */
-
/* This file lives in both GCC and libiberty. When making changes, please
try not to break either. */
@@ -44,9 +38,7 @@ Boston, MA 02110-1301, USA. */
#include "safe-ctype.h"
-#include <sys/types.h>
#include <string.h>
-#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
@@ -55,204 +47,14 @@ void * malloc ();
void * realloc ();
#endif
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-#ifndef INT_MAX
-# define INT_MAX (int)(((unsigned int) ~0) >> 1) /* 0x7FFFFFFF */
-#endif
-
#include <demangle.h>
#undef CURRENT_DEMANGLING_STYLE
-#define CURRENT_DEMANGLING_STYLE work->options
+#define CURRENT_DEMANGLING_STYLE options
#include "libiberty.h"
-#define min(X,Y) (((X) < (Y)) ? (X) : (Y))
-
-/* A value at least one greater than the maximum number of characters
- that will be output when using the `%d' format with `printf'. */
-#define INTBUF_SIZE 32
-
-extern void fancy_abort (void) ATTRIBUTE_NORETURN;
-
-/* In order to allow a single demangler executable to demangle strings
- using various common values of CPLUS_MARKER, as well as any specific
- one set at compile time, we maintain a string containing all the
- commonly used ones, and check to see if the marker we are looking for
- is in that string. CPLUS_MARKER is usually '$' on systems where the
- assembler can deal with that. Where the assembler can't, it's usually
- '.' (but on many systems '.' is used for other things). We put the
- current defined CPLUS_MARKER first (which defaults to '$'), followed
- by the next most common value, followed by an explicit '$' in case
- the value of CPLUS_MARKER is not '$'.
-
- We could avoid this if we could just get g++ to tell us what the actual
- cplus marker character is as part of the debug information, perhaps by
- ensuring that it is the character that terminates the gcc<n>_compiled
- marker symbol (FIXME). */
-
-#if !defined (CPLUS_MARKER)
-#define CPLUS_MARKER '$'
-#endif
-
enum demangling_styles current_demangling_style = auto_demangling;
-static char cplus_markers[] = { CPLUS_MARKER, '.', '$', '\0' };
-
-static char char_str[2] = { '\000', '\000' };
-
-void
-set_cplus_marker_for_demangling (int ch)
-{
- cplus_markers[0] = ch;
-}
-
-typedef struct string /* Beware: these aren't required to be */
-{ /* '\0' terminated. */
- char *b; /* pointer to start of string */
- char *p; /* pointer after last character */
- char *e; /* pointer after end of allocated space */
-} string;
-
-/* Stuff that is shared between sub-routines.
- Using a shared structure allows cplus_demangle to be reentrant. */
-
-struct work_stuff
-{
- int options;
- char **typevec;
- char **ktypevec;
- char **btypevec;
- int numk;
- int numb;
- int ksize;
- int bsize;
- int ntypes;
- int typevec_size;
- int constructor;
- int destructor;
- int static_type; /* A static member function */
- int temp_start; /* index in demangled to start of template args */
- int type_quals; /* The type qualifiers. */
- int dllimported; /* Symbol imported from a PE DLL */
- char **tmpl_argvec; /* Template function arguments. */
- int ntmpl_args; /* The number of template function arguments. */
- int forgetting_types; /* Nonzero if we are not remembering the types
- we see. */
- string* previous_argument; /* The last function argument demangled. */
- int nrepeats; /* The number of times to repeat the previous
- argument. */
- int *proctypevec; /* Indices of currently processed remembered typevecs. */
- int proctypevec_size;
- int nproctypes;
-};
-
-#define PRINT_ANSI_QUALIFIERS (work -> options & DMGL_ANSI)
-#define PRINT_ARG_TYPES (work -> options & DMGL_PARAMS)
-
-static const struct optable
-{
- const char *const in;
- const char *const out;
- const int flags;
-} optable[] = {
- {"nw", " new", DMGL_ANSI}, /* new (1.92, ansi) */
- {"dl", " delete", DMGL_ANSI}, /* new (1.92, ansi) */
- {"new", " new", 0}, /* old (1.91, and 1.x) */
- {"delete", " delete", 0}, /* old (1.91, and 1.x) */
- {"vn", " new []", DMGL_ANSI}, /* GNU, pending ansi */
- {"vd", " delete []", DMGL_ANSI}, /* GNU, pending ansi */
- {"as", "=", DMGL_ANSI}, /* ansi */
- {"ne", "!=", DMGL_ANSI}, /* old, ansi */
- {"eq", "==", DMGL_ANSI}, /* old, ansi */
- {"ge", ">=", DMGL_ANSI}, /* old, ansi */
- {"gt", ">", DMGL_ANSI}, /* old, ansi */
- {"le", "<=", DMGL_ANSI}, /* old, ansi */
- {"lt", "<", DMGL_ANSI}, /* old, ansi */
- {"plus", "+", 0}, /* old */
- {"pl", "+", DMGL_ANSI}, /* ansi */
- {"apl", "+=", DMGL_ANSI}, /* ansi */
- {"minus", "-", 0}, /* old */
- {"mi", "-", DMGL_ANSI}, /* ansi */
- {"ami", "-=", DMGL_ANSI}, /* ansi */
- {"mult", "*", 0}, /* old */
- {"ml", "*", DMGL_ANSI}, /* ansi */
- {"amu", "*=", DMGL_ANSI}, /* ansi (ARM/Lucid) */
- {"aml", "*=", DMGL_ANSI}, /* ansi (GNU/g++) */
- {"convert", "+", 0}, /* old (unary +) */
- {"negate", "-", 0}, /* old (unary -) */
- {"trunc_mod", "%", 0}, /* old */
- {"md", "%", DMGL_ANSI}, /* ansi */
- {"amd", "%=", DMGL_ANSI}, /* ansi */
- {"trunc_div", "/", 0}, /* old */
- {"dv", "/", DMGL_ANSI}, /* ansi */
- {"adv", "/=", DMGL_ANSI}, /* ansi */
- {"truth_andif", "&&", 0}, /* old */
- {"aa", "&&", DMGL_ANSI}, /* ansi */
- {"truth_orif", "||", 0}, /* old */
- {"oo", "||", DMGL_ANSI}, /* ansi */
- {"truth_not", "!", 0}, /* old */
- {"nt", "!", DMGL_ANSI}, /* ansi */
- {"postincrement","++", 0}, /* old */
- {"pp", "++", DMGL_ANSI}, /* ansi */
- {"postdecrement","--", 0}, /* old */
- {"mm", "--", DMGL_ANSI}, /* ansi */
- {"bit_ior", "|", 0}, /* old */
- {"or", "|", DMGL_ANSI}, /* ansi */
- {"aor", "|=", DMGL_ANSI}, /* ansi */
- {"bit_xor", "^", 0}, /* old */
- {"er", "^", DMGL_ANSI}, /* ansi */
- {"aer", "^=", DMGL_ANSI}, /* ansi */
- {"bit_and", "&", 0}, /* old */
- {"ad", "&", DMGL_ANSI}, /* ansi */
- {"aad", "&=", DMGL_ANSI}, /* ansi */
- {"bit_not", "~", 0}, /* old */
- {"co", "~", DMGL_ANSI}, /* ansi */
- {"call", "()", 0}, /* old */
- {"cl", "()", DMGL_ANSI}, /* ansi */
- {"alshift", "<<", 0}, /* old */
- {"ls", "<<", DMGL_ANSI}, /* ansi */
- {"als", "<<=", DMGL_ANSI}, /* ansi */
- {"arshift", ">>", 0}, /* old */
- {"rs", ">>", DMGL_ANSI}, /* ansi */
- {"ars", ">>=", DMGL_ANSI}, /* ansi */
- {"component", "->", 0}, /* old */
- {"pt", "->", DMGL_ANSI}, /* ansi; Lucid C++ form */
- {"rf", "->", DMGL_ANSI}, /* ansi; ARM/GNU form */
- {"indirect", "*", 0}, /* old */
- {"method_call", "->()", 0}, /* old */
- {"addr", "&", 0}, /* old (unary &) */
- {"array", "[]", 0}, /* old */
- {"vc", "[]", DMGL_ANSI}, /* ansi */
- {"compound", ", ", 0}, /* old */
- {"cm", ", ", DMGL_ANSI}, /* ansi */
- {"cond", "?:", 0}, /* old */
- {"cn", "?:", DMGL_ANSI}, /* pseudo-ansi */
- {"max", ">?", 0}, /* old */
- {"mx", ">?", DMGL_ANSI}, /* pseudo-ansi */
- {"min", "<?", 0}, /* old */
- {"mn", "<?", DMGL_ANSI}, /* pseudo-ansi */
- {"nop", "", 0}, /* old (for operator=) */
- {"rm", "->*", DMGL_ANSI}, /* ansi */
- {"sz", "sizeof ", DMGL_ANSI} /* pseudo-ansi */
-};
-
-/* These values are used to indicate the various type varieties.
- They are all non-zero so that they can be used as `success'
- values. */
-typedef enum type_kind_t
-{
- tk_none,
- tk_pointer,
- tk_reference,
- tk_rvalue_reference,
- tk_integral,
- tk_bool,
- tk_char,
- tk_real
-} type_kind_t;
-
const struct demangler_engine libiberty_demanglers[] =
{
{
@@ -268,39 +70,9 @@ const struct demangler_engine libiberty_demanglers[] =
}
,
{
- GNU_DEMANGLING_STYLE_STRING,
- gnu_demangling,
- "GNU (g++) style demangling"
- }
- ,
- {
- LUCID_DEMANGLING_STYLE_STRING,
- lucid_demangling,
- "Lucid (lcc) style demangling"
- }
- ,
- {
- ARM_DEMANGLING_STYLE_STRING,
- arm_demangling,
- "ARM style demangling"
- }
- ,
- {
- HP_DEMANGLING_STYLE_STRING,
- hp_demangling,
- "HP (aCC) style demangling"
- }
- ,
- {
- EDG_DEMANGLING_STYLE_STRING,
- edg_demangling,
- "EDG style demangling"
- }
- ,
- {
GNU_V3_DEMANGLING_STYLE_STRING,
gnu_v3_demangling,
- "GNU (g++) V3 ABI-style demangling"
+ "GNU (g++) V3 (Itanium C++ ABI) style demangling"
}
,
{
@@ -332,474 +104,6 @@ const struct demangler_engine libiberty_demanglers[] =
}
};
-#define STRING_EMPTY(str) ((str) -> b == (str) -> p)
-#define APPEND_BLANK(str) {if (!STRING_EMPTY(str)) \
- string_append(str, " ");}
-#define LEN_STRING(str) ( (STRING_EMPTY(str))?0:((str)->p - (str)->b))
-
-/* The scope separator appropriate for the language being demangled. */
-
-#define SCOPE_STRING(work) ((work->options & DMGL_JAVA) ? "." : "::")
-
-#define ARM_VTABLE_STRING "__vtbl__" /* Lucid/ARM virtual table prefix */
-#define ARM_VTABLE_STRLEN 8 /* strlen (ARM_VTABLE_STRING) */
-
-/* Prototypes for local functions */
-
-static void delete_work_stuff (struct work_stuff *);
-
-static void delete_non_B_K_work_stuff (struct work_stuff *);
-
-static char *mop_up (struct work_stuff *, string *, int);
-
-static void squangle_mop_up (struct work_stuff *);
-
-static void work_stuff_copy_to_from (struct work_stuff *, struct work_stuff *);
-
-#if 0
-static int
-demangle_method_args (struct work_stuff *, const char **, string *);
-#endif
-
-static char *
-internal_cplus_demangle (struct work_stuff *, const char *);
-
-static int
-demangle_template_template_parm (struct work_stuff *work,
- const char **, string *);
-
-static int
-demangle_template (struct work_stuff *work, const char **, string *,
- string *, int, int);
-
-static int
-arm_pt (struct work_stuff *, const char *, int, const char **,
- const char **);
-
-static int
-demangle_class_name (struct work_stuff *, const char **, string *);
-
-static int
-demangle_qualified (struct work_stuff *, const char **, string *,
- int, int);
-
-static int demangle_class (struct work_stuff *, const char **, string *);
-
-static int demangle_fund_type (struct work_stuff *, const char **, string *);
-
-static int demangle_signature (struct work_stuff *, const char **, string *);
-
-static int demangle_prefix (struct work_stuff *, const char **, string *);
-
-static int gnu_special (struct work_stuff *, const char **, string *);
-
-static int arm_special (const char **, string *);
-
-static void string_need (string *, int);
-
-static void string_delete (string *);
-
-static void
-string_init (string *);
-
-static void string_clear (string *);
-
-#if 0
-static int string_empty (string *);
-#endif
-
-static void string_append (string *, const char *);
-
-static void string_appends (string *, string *);
-
-static void string_appendn (string *, const char *, int);
-
-static void string_prepend (string *, const char *);
-
-static void string_prependn (string *, const char *, int);
-
-static void string_append_template_idx (string *, int);
-
-static int get_count (const char **, int *);
-
-static int consume_count (const char **);
-
-static int consume_count_with_underscores (const char**);
-
-static int demangle_args (struct work_stuff *, const char **, string *);
-
-static int demangle_nested_args (struct work_stuff*, const char**, string*);
-
-static int do_type (struct work_stuff *, const char **, string *);
-
-static int do_arg (struct work_stuff *, const char **, string *);
-
-static int
-demangle_function_name (struct work_stuff *, const char **, string *,
- const char *);
-
-static int
-iterate_demangle_function (struct work_stuff *,
- const char **, string *, const char *);
-
-static void remember_type (struct work_stuff *, const char *, int);
-
-static void push_processed_type (struct work_stuff *, int);
-
-static void pop_processed_type (struct work_stuff *);
-
-static void remember_Btype (struct work_stuff *, const char *, int, int);
-
-static int register_Btype (struct work_stuff *);
-
-static void remember_Ktype (struct work_stuff *, const char *, int);
-
-static void forget_types (struct work_stuff *);
-
-static void forget_B_and_K_types (struct work_stuff *);
-
-static void string_prepends (string *, string *);
-
-static int
-demangle_template_value_parm (struct work_stuff*, const char**,
- string*, type_kind_t);
-
-static int
-do_hpacc_template_const_value (struct work_stuff *, const char **, string *);
-
-static int
-do_hpacc_template_literal (struct work_stuff *, const char **, string *);
-
-static int snarf_numeric_literal (const char **, string *);
-
-/* There is a TYPE_QUAL value for each type qualifier. They can be
- combined by bitwise-or to form the complete set of qualifiers for a
- type. */
-
-#define TYPE_UNQUALIFIED 0x0
-#define TYPE_QUAL_CONST 0x1
-#define TYPE_QUAL_VOLATILE 0x2
-#define TYPE_QUAL_RESTRICT 0x4
-
-static int code_for_qualifier (int);
-
-static const char* qualifier_string (int);
-
-static const char* demangle_qualifier (int);
-
-static int demangle_expression (struct work_stuff *, const char **, string *,
- type_kind_t);
-
-static int
-demangle_integral_value (struct work_stuff *, const char **, string *);
-
-static int
-demangle_real_value (struct work_stuff *, const char **, string *);
-
-static void
-demangle_arm_hp_template (struct work_stuff *, const char **, int, string *);
-
-static void
-recursively_demangle (struct work_stuff *, const char **, string *, int);
-
-/* Translate count to integer, consuming tokens in the process.
- Conversion terminates on the first non-digit character.
-
- Trying to consume something that isn't a count results in no
- consumption of input and a return of -1.
-
- Overflow consumes the rest of the digits, and returns -1. */
-
-static int
-consume_count (const char **type)
-{
- int count = 0;
-
- if (! ISDIGIT ((unsigned char)**type))
- return -1;
-
- while (ISDIGIT ((unsigned char)**type))
- {
- const int digit = **type - '0';
- /* Check for overflow. */
- if (count > ((INT_MAX - digit) / 10))
- {
- while (ISDIGIT ((unsigned char) **type))
- (*type)++;
- return -1;
- }
-
- count *= 10;
- count += digit;
- (*type)++;
- }
-
- if (count < 0)
- count = -1;
-
- return (count);
-}
-
-
-/* Like consume_count, but for counts that are preceded and followed
- by '_' if they are greater than 10. Also, -1 is returned for
- failure, since 0 can be a valid value. */
-
-static int
-consume_count_with_underscores (const char **mangled)
-{
- int idx;
-
- if (**mangled == '_')
- {
- (*mangled)++;
- if (!ISDIGIT ((unsigned char)**mangled))
- return -1;
-
- idx = consume_count (mangled);
- if (**mangled != '_')
- /* The trailing underscore was missing. */
- return -1;
-
- (*mangled)++;
- }
- else
- {
- if (**mangled < '0' || **mangled > '9')
- return -1;
-
- idx = **mangled - '0';
- (*mangled)++;
- }
-
- return idx;
-}
-
-/* C is the code for a type-qualifier. Return the TYPE_QUAL
- corresponding to this qualifier. */
-
-static int
-code_for_qualifier (int c)
-{
- switch (c)
- {
- case 'C':
- return TYPE_QUAL_CONST;
-
- case 'V':
- return TYPE_QUAL_VOLATILE;
-
- case 'u':
- return TYPE_QUAL_RESTRICT;
-
- default:
- break;
- }
-
- /* C was an invalid qualifier. */
- abort ();
-}
-
-/* Return the string corresponding to the qualifiers given by
- TYPE_QUALS. */
-
-static const char*
-qualifier_string (int type_quals)
-{
- switch (type_quals)
- {
- case TYPE_UNQUALIFIED:
- return "";
-
- case TYPE_QUAL_CONST:
- return "const";
-
- case TYPE_QUAL_VOLATILE:
- return "volatile";
-
- case TYPE_QUAL_RESTRICT:
- return "__restrict";
-
- case TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE:
- return "const volatile";
-
- case TYPE_QUAL_CONST | TYPE_QUAL_RESTRICT:
- return "const __restrict";
-
- case TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT:
- return "volatile __restrict";
-
- case TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT:
- return "const volatile __restrict";
-
- default:
- break;
- }
-
- /* TYPE_QUALS was an invalid qualifier set. */
- abort ();
-}
-
-/* C is the code for a type-qualifier. Return the string
- corresponding to this qualifier. This function should only be
- called with a valid qualifier code. */
-
-static const char*
-demangle_qualifier (int c)
-{
- return qualifier_string (code_for_qualifier (c));
-}
-
-int
-cplus_demangle_opname (const char *opname, char *result, int options)
-{
- int len, len1, ret;
- string type;
- struct work_stuff work[1];
- const char *tem;
-
- len = strlen(opname);
- result[0] = '\0';
- ret = 0;
- memset ((char *) work, 0, sizeof (work));
- work->options = options;
-
- if (opname[0] == '_' && opname[1] == '_'
- && opname[2] == 'o' && opname[3] == 'p')
- {
- /* ANSI. */
- /* type conversion operator. */
- tem = opname + 4;
- if (do_type (work, &tem, &type))
- {
- strcat (result, "operator ");
- strncat (result, type.b, type.p - type.b);
- string_delete (&type);
- ret = 1;
- }
- }
- else if (opname[0] == '_' && opname[1] == '_'
- && ISLOWER((unsigned char)opname[2])
- && ISLOWER((unsigned char)opname[3]))
- {
- if (opname[4] == '\0')
- {
- /* Operator. */
- size_t i;
- for (i = 0; i < ARRAY_SIZE (optable); i++)
- {
- if (strlen (optable[i].in) == 2
- && memcmp (optable[i].in, opname + 2, 2) == 0)
- {
- strcat (result, "operator");
- strcat (result, optable[i].out);
- ret = 1;
- break;
- }
- }
- }
- else
- {
- if (opname[2] == 'a' && opname[5] == '\0')
- {
- /* Assignment. */
- size_t i;
- for (i = 0; i < ARRAY_SIZE (optable); i++)
- {
- if (strlen (optable[i].in) == 3
- && memcmp (optable[i].in, opname + 2, 3) == 0)
- {
- strcat (result, "operator");
- strcat (result, optable[i].out);
- ret = 1;
- break;
- }
- }
- }
- }
- }
- else if (len >= 3
- && opname[0] == 'o'
- && opname[1] == 'p'
- && strchr (cplus_markers, opname[2]) != NULL)
- {
- /* see if it's an assignment expression */
- if (len >= 10 /* op$assign_ */
- && memcmp (opname + 3, "assign_", 7) == 0)
- {
- size_t i;
- for (i = 0; i < ARRAY_SIZE (optable); i++)
- {
- len1 = len - 10;
- if ((int) strlen (optable[i].in) == len1
- && memcmp (optable[i].in, opname + 10, len1) == 0)
- {
- strcat (result, "operator");
- strcat (result, optable[i].out);
- strcat (result, "=");
- ret = 1;
- break;
- }
- }
- }
- else
- {
- size_t i;
- for (i = 0; i < ARRAY_SIZE (optable); i++)
- {
- len1 = len - 3;
- if ((int) strlen (optable[i].in) == len1
- && memcmp (optable[i].in, opname + 3, len1) == 0)
- {
- strcat (result, "operator");
- strcat (result, optable[i].out);
- ret = 1;
- break;
- }
- }
- }
- }
- else if (len >= 5 && memcmp (opname, "type", 4) == 0
- && strchr (cplus_markers, opname[4]) != NULL)
- {
- /* type conversion operator */
- tem = opname + 5;
- if (do_type (work, &tem, &type))
- {
- strcat (result, "operator ");
- strncat (result, type.b, type.p - type.b);
- string_delete (&type);
- ret = 1;
- }
- }
- squangle_mop_up (work);
- return ret;
-
-}
-
-/* Takes operator name as e.g. "++" and returns mangled
- operator name (e.g. "postincrement_expr"), or NULL if not found.
-
- If OPTIONS & DMGL_ANSI == 1, return the ANSI name;
- if OPTIONS & DMGL_ANSI == 0, return the old GNU name. */
-
-const char *
-cplus_mangle_opname (const char *opname, int options)
-{
- size_t i;
- int len;
-
- len = strlen (opname);
- for (i = 0; i < ARRAY_SIZE (optable); i++)
- {
- if ((int) strlen (optable[i].out) == len
- && (options & DMGL_ANSI) == (optable[i].flags & DMGL_ANSI)
- && memcmp (optable[i].out, opname, len) == 0)
- return optable[i].in;
- }
- return (0);
-}
-
/* Add a routine to set the demangling style to be sure it is valid and
allow for any demangler initialization that maybe necessary. */
@@ -840,22 +144,6 @@ cplus_demangle_name_to_style (const char *name)
It is the caller's responsibility to free the string which
is returned.
- The OPTIONS arg may contain one or more of the following bits:
-
- DMGL_ANSI ANSI qualifiers such as `const' and `void' are
- included.
- DMGL_PARAMS Function parameters are included.
-
- For example,
-
- cplus_demangle ("foo__1Ai", DMGL_PARAMS) => "A::foo(int)"
- cplus_demangle ("foo__1Ai", DMGL_PARAMS | DMGL_ANSI) => "A::foo(int)"
- cplus_demangle ("foo__1Ai", 0) => "A::foo"
-
- cplus_demangle ("foo__1Afe", DMGL_PARAMS) => "A::foo(float,...)"
- cplus_demangle ("foo__1Afe", DMGL_PARAMS | DMGL_ANSI)=> "A::foo(float,...)"
- cplus_demangle ("foo__1Afe", 0) => "A::foo"
-
Note that any leading underscores, or other such characters prepended by
the compilation system, are presumed to have already been stripped from
MANGLED. */
@@ -864,37 +152,27 @@ char *
cplus_demangle (const char *mangled, int options)
{
char *ret;
- struct work_stuff work[1];
if (current_demangling_style == no_demangling)
return xstrdup (mangled);
- memset ((char *) work, 0, sizeof (work));
- work->options = options;
- if ((work->options & DMGL_STYLE_MASK) == 0)
- work->options |= (int) current_demangling_style & DMGL_STYLE_MASK;
+ if ((options & DMGL_STYLE_MASK) == 0)
+ options |= (int) current_demangling_style & DMGL_STYLE_MASK;
- /* The V3 ABI demangling is implemented elsewhere. */
- if (GNU_V3_DEMANGLING || RUST_DEMANGLING || AUTO_DEMANGLING)
+ /* The Rust demangling is implemented elsewhere.
+ Legacy Rust symbols overlap with GNU_V3, so try Rust first. */
+ if (RUST_DEMANGLING || AUTO_DEMANGLING)
{
- ret = cplus_demangle_v3 (mangled, work->options);
- if (GNU_V3_DEMANGLING)
- return ret;
-
- if (ret)
- {
- /* Rust symbols are GNU_V3 mangled plus some extra subtitutions.
- The subtitutions are always smaller, so do in place changes. */
- if (rust_is_mangled (ret))
- rust_demangle_sym (ret);
- else if (RUST_DEMANGLING)
- {
- free (ret);
- ret = NULL;
- }
- }
-
+ ret = rust_demangle (mangled, options);
if (ret || RUST_DEMANGLING)
+ return ret;
+ }
+
+ /* The V3 ABI demangling is implemented elsewhere. */
+ if (GNU_V3_DEMANGLING || AUTO_DEMANGLING)
+ {
+ ret = cplus_demangle_v3 (mangled, options);
+ if (ret || GNU_V3_DEMANGLING)
return ret;
}
@@ -915,32 +193,9 @@ cplus_demangle (const char *mangled, int options)
return ret;
}
- ret = internal_cplus_demangle (work, mangled);
- squangle_mop_up (work);
return (ret);
}
-char *
-rust_demangle (const char *mangled, int options)
-{
- /* Rust symbols are GNU_V3 mangled plus some extra subtitutions. */
- char *ret = cplus_demangle_v3 (mangled, options);
-
- /* The Rust subtitutions are always smaller, so do in place changes. */
- if (ret != NULL)
- {
- if (rust_is_mangled (ret))
- rust_demangle_sym (ret);
- else
- {
- free (ret);
- ret = NULL;
- }
- }
-
- return ret;
-}
-
/* Demangle ada names. The encoding is documented in gcc/ada/exp_dbug.ads. */
char *
@@ -1205,3828 +460,3 @@ ada_demangle (const char *mangled, int option ATTRIBUTE_UNUSED)
return demangled;
}
-
-/* This function performs most of what cplus_demangle use to do, but
- to be able to demangle a name with a B, K or n code, we need to
- have a longer term memory of what types have been seen. The original
- now initializes and cleans up the squangle code info, while internal
- calls go directly to this routine to avoid resetting that info. */
-
-static char *
-internal_cplus_demangle (struct work_stuff *work, const char *mangled)
-{
-
- string decl;
- int success = 0;
- char *demangled = NULL;
- int s1, s2, s3, s4;
- s1 = work->constructor;
- s2 = work->destructor;
- s3 = work->static_type;
- s4 = work->type_quals;
- work->constructor = work->destructor = 0;
- work->type_quals = TYPE_UNQUALIFIED;
- work->dllimported = 0;
-
- if ((mangled != NULL) && (*mangled != '\0'))
- {
- string_init (&decl);
-
- /* First check to see if gnu style demangling is active and if the
- string to be demangled contains a CPLUS_MARKER. If so, attempt to
- recognize one of the gnu special forms rather than looking for a
- standard prefix. In particular, don't worry about whether there
- is a "__" string in the mangled string. Consider "_$_5__foo" for
- example. */
-
- if ((AUTO_DEMANGLING || GNU_DEMANGLING))
- {
- success = gnu_special (work, &mangled, &decl);
- if (!success)
- {
- delete_work_stuff (work);
- string_delete (&decl);
- }
- }
- if (!success)
- {
- success = demangle_prefix (work, &mangled, &decl);
- }
- if (success && (*mangled != '\0'))
- {
- success = demangle_signature (work, &mangled, &decl);
- }
- if (work->constructor == 2)
- {
- string_prepend (&decl, "global constructors keyed to ");
- work->constructor = 0;
- }
- else if (work->destructor == 2)
- {
- string_prepend (&decl, "global destructors keyed to ");
- work->destructor = 0;
- }
- else if (work->dllimported == 1)
- {
- string_prepend (&decl, "import stub for ");
- work->dllimported = 0;
- }
- demangled = mop_up (work, &decl, success);
- }
- work->constructor = s1;
- work->destructor = s2;
- work->static_type = s3;
- work->type_quals = s4;
- return demangled;
-}
-
-
-/* Clear out and squangling related storage */
-static void
-squangle_mop_up (struct work_stuff *work)
-{
- /* clean up the B and K type mangling types. */
- forget_B_and_K_types (work);
- if (work -> btypevec != NULL)
- {
- free ((char *) work -> btypevec);
- work->btypevec = NULL;
- work->bsize = 0;
- }
- if (work -> ktypevec != NULL)
- {
- free ((char *) work -> ktypevec);
- work->ktypevec = NULL;
- work->ksize = 0;
- }
-}
-
-
-/* Copy the work state and storage. */
-
-static void
-work_stuff_copy_to_from (struct work_stuff *to, struct work_stuff *from)
-{
- int i;
-
- delete_work_stuff (to);
-
- /* Shallow-copy scalars. */
- memcpy (to, from, sizeof (*to));
-
- /* Deep-copy dynamic storage. */
- if (from->typevec_size)
- to->typevec = XNEWVEC (char *, from->typevec_size);
-
- for (i = 0; i < from->ntypes; i++)
- {
- int len = strlen (from->typevec[i]) + 1;
-
- to->typevec[i] = XNEWVEC (char, len);
- memcpy (to->typevec[i], from->typevec[i], len);
- }
-
- if (from->ksize)
- to->ktypevec = XNEWVEC (char *, from->ksize);
-
- for (i = 0; i < from->numk; i++)
- {
- int len = strlen (from->ktypevec[i]) + 1;
-
- to->ktypevec[i] = XNEWVEC (char, len);
- memcpy (to->ktypevec[i], from->ktypevec[i], len);
- }
-
- if (from->bsize)
- to->btypevec = XNEWVEC (char *, from->bsize);
-
- for (i = 0; i < from->numb; i++)
- {
- int len = strlen (from->btypevec[i]) + 1;
-
- to->btypevec[i] = XNEWVEC (char , len);
- memcpy (to->btypevec[i], from->btypevec[i], len);
- }
-
- if (from->proctypevec)
- to->proctypevec =
- XDUPVEC (int, from->proctypevec, from->proctypevec_size);
-
- if (from->ntmpl_args)
- to->tmpl_argvec = XNEWVEC (char *, from->ntmpl_args);
-
- for (i = 0; i < from->ntmpl_args; i++)
- {
- int len = strlen (from->tmpl_argvec[i]) + 1;
-
- to->tmpl_argvec[i] = XNEWVEC (char, len);
- memcpy (to->tmpl_argvec[i], from->tmpl_argvec[i], len);
- }
-
- if (from->previous_argument)
- {
- to->previous_argument = XNEW (string);
- string_init (to->previous_argument);
- string_appends (to->previous_argument, from->previous_argument);
- }
-}
-
-
-/* Delete dynamic stuff in work_stuff that is not to be re-used. */
-
-static void
-delete_non_B_K_work_stuff (struct work_stuff *work)
-{
- /* Discard the remembered types, if any. */
-
- forget_types (work);
- if (work->typevec != NULL)
- {
- free ((char *) work->typevec);
- work->typevec = NULL;
- work->typevec_size = 0;
- }
- if (work->proctypevec != NULL)
- {
- free (work->proctypevec);
- work->proctypevec = NULL;
- work->proctypevec_size = 0;
- }
- if (work->tmpl_argvec)
- {
- int i;
-
- for (i = 0; i < work->ntmpl_args; i++)
- free ((char*) work->tmpl_argvec[i]);
-
- free ((char*) work->tmpl_argvec);
- work->tmpl_argvec = NULL;
- }
- if (work->previous_argument)
- {
- string_delete (work->previous_argument);
- free ((char*) work->previous_argument);
- work->previous_argument = NULL;
- }
-}
-
-
-/* Delete all dynamic storage in work_stuff. */
-static void
-delete_work_stuff (struct work_stuff *work)
-{
- delete_non_B_K_work_stuff (work);
- squangle_mop_up (work);
-}
-
-
-/* Clear out any mangled storage */
-
-static char *
-mop_up (struct work_stuff *work, string *declp, int success)
-{
- char *demangled = NULL;
-
- delete_non_B_K_work_stuff (work);
-
- /* If demangling was successful, ensure that the demangled string is null
- terminated and return it. Otherwise, free the demangling decl. */
-
- if (!success)
- {
- string_delete (declp);
- }
- else
- {
- string_appendn (declp, "", 1);
- demangled = declp->b;
- }
- return (demangled);
-}
-
-/*
-
-LOCAL FUNCTION
-
- demangle_signature -- demangle the signature part of a mangled name
-
-SYNOPSIS
-
- static int
- demangle_signature (struct work_stuff *work, const char **mangled,
- string *declp);
-
-DESCRIPTION
-
- Consume and demangle the signature portion of the mangled name.
-
- DECLP is the string where demangled output is being built. At
- entry it contains the demangled root name from the mangled name
- prefix. I.E. either a demangled operator name or the root function
- name. In some special cases, it may contain nothing.
-
- *MANGLED points to the current unconsumed location in the mangled
- name. As tokens are consumed and demangling is performed, the
- pointer is updated to continuously point at the next token to
- be consumed.
-
- Demangling GNU style mangled names is nasty because there is no
- explicit token that marks the start of the outermost function
- argument list. */
-
-static int
-demangle_signature (struct work_stuff *work,
- const char **mangled, string *declp)
-{
- int success = 1;
- int func_done = 0;
- int expect_func = 0;
- int expect_return_type = 0;
- const char *oldmangled = NULL;
- string trawname;
- string tname;
-
- while (success && (**mangled != '\0'))
- {
- switch (**mangled)
- {
- case 'Q':
- oldmangled = *mangled;
- success = demangle_qualified (work, mangled, declp, 1, 0);
- if (success)
- remember_type (work, oldmangled, *mangled - oldmangled);
- if (AUTO_DEMANGLING || GNU_DEMANGLING)
- expect_func = 1;
- oldmangled = NULL;
- break;
-
- case 'K':
- oldmangled = *mangled;
- success = demangle_qualified (work, mangled, declp, 1, 0);
- if (AUTO_DEMANGLING || GNU_DEMANGLING)
- {
- expect_func = 1;
- }
- oldmangled = NULL;
- break;
-
- case 'S':
- /* Static member function */
- if (oldmangled == NULL)
- {
- oldmangled = *mangled;
- }
- (*mangled)++;
- work -> static_type = 1;
- break;
-
- case 'C':
- case 'V':
- case 'u':
- work->type_quals |= code_for_qualifier (**mangled);
-
- /* a qualified member function */
- if (oldmangled == NULL)
- oldmangled = *mangled;
- (*mangled)++;
- break;
-
- case 'L':
- /* Local class name follows after "Lnnn_" */
- if (HP_DEMANGLING)
- {
- while (**mangled && (**mangled != '_'))
- (*mangled)++;
- if (!**mangled)
- success = 0;
- else
- (*mangled)++;
- }
- else
- success = 0;
- break;
-
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- if (oldmangled == NULL)
- {
- oldmangled = *mangled;
- }
- work->temp_start = -1; /* uppermost call to demangle_class */
- success = demangle_class (work, mangled, declp);
- if (success)
- {
- remember_type (work, oldmangled, *mangled - oldmangled);
- }
- if (AUTO_DEMANGLING || GNU_DEMANGLING || EDG_DEMANGLING)
- {
- /* EDG and others will have the "F", so we let the loop cycle
- if we are looking at one. */
- if (**mangled != 'F')
- expect_func = 1;
- }
- oldmangled = NULL;
- break;
-
- case 'B':
- {
- string s;
- success = do_type (work, mangled, &s);
- if (success)
- {
- string_append (&s, SCOPE_STRING (work));
- string_prepends (declp, &s);
- string_delete (&s);
- }
- oldmangled = NULL;
- expect_func = 1;
- }
- break;
-
- case 'F':
- /* Function */
- /* ARM/HP style demangling includes a specific 'F' character after
- the class name. For GNU style, it is just implied. So we can
- safely just consume any 'F' at this point and be compatible
- with either style. */
-
- oldmangled = NULL;
- func_done = 1;
- (*mangled)++;
-
- /* For lucid/ARM/HP style we have to forget any types we might
- have remembered up to this point, since they were not argument
- types. GNU style considers all types seen as available for
- back references. See comment in demangle_args() */
-
- if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING)
- {
- forget_types (work);
- }
- success = demangle_args (work, mangled, declp);
- /* After picking off the function args, we expect to either
- find the function return type (preceded by an '_') or the
- end of the string. */
- if (success && (AUTO_DEMANGLING || EDG_DEMANGLING) && **mangled == '_')
- {
- ++(*mangled);
- /* At this level, we do not care about the return type. */
- success = do_type (work, mangled, &tname);
- string_delete (&tname);
- }
-
- break;
-
- case 't':
- /* G++ Template */
- string_init(&trawname);
- string_init(&tname);
- if (oldmangled == NULL)
- {
- oldmangled = *mangled;
- }
- success = demangle_template (work, mangled, &tname,
- &trawname, 1, 1);
- if (success)
- {
- remember_type (work, oldmangled, *mangled - oldmangled);
- }
- string_append (&tname, SCOPE_STRING (work));
-
- string_prepends(declp, &tname);
- if (work -> destructor & 1)
- {
- string_prepend (&trawname, "~");
- string_appends (declp, &trawname);
- work->destructor -= 1;
- }
- if ((work->constructor & 1) || (work->destructor & 1))
- {
- string_appends (declp, &trawname);
- work->constructor -= 1;
- }
- string_delete(&trawname);
- string_delete(&tname);
- oldmangled = NULL;
- expect_func = 1;
- break;
-
- case '_':
- if ((AUTO_DEMANGLING || GNU_DEMANGLING) && expect_return_type)
- {
- /* Read the return type. */
- string return_type;
-
- (*mangled)++;
- success = do_type (work, mangled, &return_type);
- APPEND_BLANK (&return_type);
-
- string_prepends (declp, &return_type);
- string_delete (&return_type);
- break;
- }
- else
- /* At the outermost level, we cannot have a return type specified,
- so if we run into another '_' at this point we are dealing with
- a mangled name that is either bogus, or has been mangled by
- some algorithm we don't know how to deal with. So just
- reject the entire demangling. */
- /* However, "_nnn" is an expected suffix for alternate entry point
- numbered nnn for a function, with HP aCC, so skip over that
- without reporting failure. pai/1997-09-04 */
- if (HP_DEMANGLING)
- {
- (*mangled)++;
- while (**mangled && ISDIGIT ((unsigned char)**mangled))
- (*mangled)++;
- }
- else
- success = 0;
- break;
-
- case 'H':
- if (AUTO_DEMANGLING || GNU_DEMANGLING)
- {
- /* A G++ template function. Read the template arguments. */
- success = demangle_template (work, mangled, declp, 0, 0,
- 0);
- if (!(work->constructor & 1))
- expect_return_type = 1;
- if (!**mangled)
- success = 0;
- else
- (*mangled)++;
- break;
- }
- /* fall through */
-
- default:
- if (AUTO_DEMANGLING || GNU_DEMANGLING)
- {
- /* Assume we have stumbled onto the first outermost function
- argument token, and start processing args. */
- func_done = 1;
- success = demangle_args (work, mangled, declp);
- }
- else
- {
- /* Non-GNU demanglers use a specific token to mark the start
- of the outermost function argument tokens. Typically 'F',
- for ARM/HP-demangling, for example. So if we find something
- we are not prepared for, it must be an error. */
- success = 0;
- }
- break;
- }
- /*
- if (AUTO_DEMANGLING || GNU_DEMANGLING)
- */
- {
- if (success && expect_func)
- {
- func_done = 1;
- if (LUCID_DEMANGLING || ARM_DEMANGLING || EDG_DEMANGLING)
- {
- forget_types (work);
- }
- success = demangle_args (work, mangled, declp);
- /* Since template include the mangling of their return types,
- we must set expect_func to 0 so that we don't try do
- demangle more arguments the next time we get here. */
- expect_func = 0;
- }
- }
- }
- if (success && !func_done)
- {
- if (AUTO_DEMANGLING || GNU_DEMANGLING)
- {
- /* With GNU style demangling, bar__3foo is 'foo::bar(void)', and
- bar__3fooi is 'foo::bar(int)'. We get here when we find the
- first case, and need to ensure that the '(void)' gets added to
- the current declp. Note that with ARM/HP, the first case
- represents the name of a static data member 'foo::bar',
- which is in the current declp, so we leave it alone. */
- success = demangle_args (work, mangled, declp);
- }
- }
- if (success && PRINT_ARG_TYPES)
- {
- if (work->static_type)
- string_append (declp, " static");
- if (work->type_quals != TYPE_UNQUALIFIED)
- {
- APPEND_BLANK (declp);
- string_append (declp, qualifier_string (work->type_quals));
- }
- }
-
- return (success);
-}
-
-#if 0
-
-static int
-demangle_method_args (struct work_stuff *work, const char **mangled,
- string *declp)
-{
- int success = 0;
-
- if (work -> static_type)
- {
- string_append (declp, *mangled + 1);
- *mangled += strlen (*mangled);
- success = 1;
- }
- else
- {
- success = demangle_args (work, mangled, declp);
- }
- return (success);
-}
-
-#endif
-
-static int
-demangle_template_template_parm (struct work_stuff *work,
- const char **mangled, string *tname)
-{
- int i;
- int r;
- int need_comma = 0;
- int success = 1;
- string temp;
-
- string_append (tname, "template <");
- /* get size of template parameter list */
- if (get_count (mangled, &r))
- {
- for (i = 0; i < r; i++)
- {
- if (need_comma)
- {
- string_append (tname, ", ");
- }
-
- /* Z for type parameters */
- if (**mangled == 'Z')
- {
- (*mangled)++;
- string_append (tname, "class");
- }
- /* z for template parameters */
- else if (**mangled == 'z')
- {
- (*mangled)++;
- success =
- demangle_template_template_parm (work, mangled, tname);
- if (!success)
- {
- break;
- }
- }
- else
- {
- /* temp is initialized in do_type */
- success = do_type (work, mangled, &temp);
- if (success)
- {
- string_appends (tname, &temp);
- }
- string_delete(&temp);
- if (!success)
- {
- break;
- }
- }
- need_comma = 1;
- }
-
- }
- if (tname->p[-1] == '>')
- string_append (tname, " ");
- string_append (tname, "> class");
- return (success);
-}
-
-static int
-demangle_expression (struct work_stuff *work, const char **mangled,
- string *s, type_kind_t tk)
-{
- int need_operator = 0;
- int success;
-
- success = 1;
- string_appendn (s, "(", 1);
- (*mangled)++;
- while (success && **mangled != 'W' && **mangled != '\0')
- {
- if (need_operator)
- {
- size_t i;
- size_t len;
-
- success = 0;
-
- len = strlen (*mangled);
-
- for (i = 0; i < ARRAY_SIZE (optable); ++i)
- {
- size_t l = strlen (optable[i].in);
-
- if (l <= len
- && memcmp (optable[i].in, *mangled, l) == 0)
- {
- string_appendn (s, " ", 1);
- string_append (s, optable[i].out);
- string_appendn (s, " ", 1);
- success = 1;
- (*mangled) += l;
- break;
- }
- }
-
- if (!success)
- break;
- }
- else
- need_operator = 1;
-
- success = demangle_template_value_parm (work, mangled, s, tk);
- }
-
- if (**mangled != 'W')
- success = 0;
- else
- {
- string_appendn (s, ")", 1);
- (*mangled)++;
- }
-
- return success;
-}
-
-static int
-demangle_integral_value (struct work_stuff *work,
- const char **mangled, string *s)
-{
- int success;
-
- if (**mangled == 'E')
- success = demangle_expression (work, mangled, s, tk_integral);
- else if (**mangled == 'Q' || **mangled == 'K')
- success = demangle_qualified (work, mangled, s, 0, 1);
- else
- {
- int value;
-
- /* By default, we let the number decide whether we shall consume an
- underscore. */
- int multidigit_without_leading_underscore = 0;
- int leave_following_underscore = 0;
-
- success = 0;
-
- if (**mangled == '_')
- {
- if (mangled[0][1] == 'm')
- {
- /* Since consume_count_with_underscores does not handle the
- `m'-prefix we must do it here, using consume_count and
- adjusting underscores: we have to consume the underscore
- matching the prepended one. */
- multidigit_without_leading_underscore = 1;
- string_appendn (s, "-", 1);
- (*mangled) += 2;
- }
- else
- {
- /* Do not consume a following underscore;
- consume_count_with_underscores will consume what
- should be consumed. */
- leave_following_underscore = 1;
- }
- }
- else
- {
- /* Negative numbers are indicated with a leading `m'. */
- if (**mangled == 'm')
- {
- string_appendn (s, "-", 1);
- (*mangled)++;
- }
- /* Since consume_count_with_underscores does not handle
- multi-digit numbers that do not start with an underscore,
- and this number can be an integer template parameter,
- we have to call consume_count. */
- multidigit_without_leading_underscore = 1;
- /* These multi-digit numbers never end on an underscore,
- so if there is one then don't eat it. */
- leave_following_underscore = 1;
- }
-
- /* We must call consume_count if we expect to remove a trailing
- underscore, since consume_count_with_underscores expects
- the leading underscore (that we consumed) if it is to handle
- multi-digit numbers. */
- if (multidigit_without_leading_underscore)
- value = consume_count (mangled);
- else
- value = consume_count_with_underscores (mangled);
-
- if (value != -1)
- {
- char buf[INTBUF_SIZE];
- sprintf (buf, "%d", value);
- string_append (s, buf);
-
- /* Numbers not otherwise delimited, might have an underscore
- appended as a delimeter, which we should skip.
-
- ??? This used to always remove a following underscore, which
- is wrong. If other (arbitrary) cases are followed by an
- underscore, we need to do something more radical. */
-
- if ((value > 9 || multidigit_without_leading_underscore)
- && ! leave_following_underscore
- && **mangled == '_')
- (*mangled)++;
-
- /* All is well. */
- success = 1;
- }
- }
-
- return success;
-}
-
-/* Demangle the real value in MANGLED. */
-
-static int
-demangle_real_value (struct work_stuff *work,
- const char **mangled, string *s)
-{
- if (**mangled == 'E')
- return demangle_expression (work, mangled, s, tk_real);
-
- if (**mangled == 'm')
- {
- string_appendn (s, "-", 1);
- (*mangled)++;
- }
- while (ISDIGIT ((unsigned char)**mangled))
- {
- string_appendn (s, *mangled, 1);
- (*mangled)++;
- }
- if (**mangled == '.') /* fraction */
- {
- string_appendn (s, ".", 1);
- (*mangled)++;
- while (ISDIGIT ((unsigned char)**mangled))
- {
- string_appendn (s, *mangled, 1);
- (*mangled)++;
- }
- }
- if (**mangled == 'e') /* exponent */
- {
- string_appendn (s, "e", 1);
- (*mangled)++;
- while (ISDIGIT ((unsigned char)**mangled))
- {
- string_appendn (s, *mangled, 1);
- (*mangled)++;
- }
- }
-
- return 1;
-}
-
-static int
-demangle_template_value_parm (struct work_stuff *work, const char **mangled,
- string *s, type_kind_t tk)
-{
- int success = 1;
-
- if (**mangled == 'Y')
- {
- /* The next argument is a template parameter. */
- int idx;
-
- (*mangled)++;
- idx = consume_count_with_underscores (mangled);
- if (idx == -1
- || (work->tmpl_argvec && idx >= work->ntmpl_args)
- || consume_count_with_underscores (mangled) == -1)
- return -1;
- if (work->tmpl_argvec)
- string_append (s, work->tmpl_argvec[idx]);
- else
- string_append_template_idx (s, idx);
- }
- else if (tk == tk_integral)
- success = demangle_integral_value (work, mangled, s);
- else if (tk == tk_char)
- {
- char tmp[2];
- int val;
- if (**mangled == 'm')
- {
- string_appendn (s, "-", 1);
- (*mangled)++;
- }
- string_appendn (s, "'", 1);
- val = consume_count(mangled);
- if (val <= 0)
- success = 0;
- else
- {
- tmp[0] = (char)val;
- tmp[1] = '\0';
- string_appendn (s, &tmp[0], 1);
- string_appendn (s, "'", 1);
- }
- }
- else if (tk == tk_bool)
- {
- int val = consume_count (mangled);
- if (val == 0)
- string_appendn (s, "false", 5);
- else if (val == 1)
- string_appendn (s, "true", 4);
- else
- success = 0;
- }
- else if (tk == tk_real)
- success = demangle_real_value (work, mangled, s);
- else if (tk == tk_pointer || tk == tk_reference
- || tk == tk_rvalue_reference)
- {
- if (**mangled == 'Q')
- success = demangle_qualified (work, mangled, s,
- /*isfuncname=*/0,
- /*append=*/1);
- else
- {
- int symbol_len = consume_count (mangled);
- if (symbol_len == -1
- || symbol_len > (long) strlen (*mangled))
- return -1;
- if (symbol_len == 0)
- string_appendn (s, "0", 1);
- else
- {
- char *p = XNEWVEC (char, symbol_len + 1), *q;
- strncpy (p, *mangled, symbol_len);
- p [symbol_len] = '\0';
- /* We use cplus_demangle here, rather than
- internal_cplus_demangle, because the name of the entity
- mangled here does not make use of any of the squangling
- or type-code information we have built up thus far; it is
- mangled independently. */
- q = cplus_demangle (p, work->options);
- if (tk == tk_pointer)
- string_appendn (s, "&", 1);
- /* FIXME: Pointer-to-member constants should get a
- qualifying class name here. */
- if (q)
- {
- string_append (s, q);
- free (q);
- }
- else
- string_append (s, p);
- free (p);
- }
- *mangled += symbol_len;
- }
- }
-
- return success;
-}
-
-/* Demangle the template name in MANGLED. The full name of the
- template (e.g., S<int>) is placed in TNAME. The name without the
- template parameters (e.g. S) is placed in TRAWNAME if TRAWNAME is
- non-NULL. If IS_TYPE is nonzero, this template is a type template,
- not a function template. If both IS_TYPE and REMEMBER are nonzero,
- the template is remembered in the list of back-referenceable
- types. */
-
-static int
-demangle_template (struct work_stuff *work, const char **mangled,
- string *tname, string *trawname,
- int is_type, int remember)
-{
- int i;
- int r;
- int need_comma = 0;
- int success = 0;
- int is_java_array = 0;
- string temp;
-
- (*mangled)++;
- if (is_type)
- {
- /* get template name */
- if (**mangled == 'z')
- {
- int idx;
- (*mangled)++;
- if (**mangled == '\0')
- return (0);
- (*mangled)++;
-
- idx = consume_count_with_underscores (mangled);
- if (idx == -1
- || (work->tmpl_argvec && idx >= work->ntmpl_args)
- || consume_count_with_underscores (mangled) == -1)
- return (0);
-
- if (work->tmpl_argvec)
- {
- string_append (tname, work->tmpl_argvec[idx]);
- if (trawname)
- string_append (trawname, work->tmpl_argvec[idx]);
- }
- else
- {
- string_append_template_idx (tname, idx);
- if (trawname)
- string_append_template_idx (trawname, idx);
- }
- }
- else
- {
- if ((r = consume_count (mangled)) <= 0
- || (int) strlen (*mangled) < r)
- {
- return (0);
- }
- is_java_array = (work -> options & DMGL_JAVA)
- && strncmp (*mangled, "JArray1Z", 8) == 0;
- if (! is_java_array)
- {
- string_appendn (tname, *mangled, r);
- }
- if (trawname)
- string_appendn (trawname, *mangled, r);
- *mangled += r;
- }
- }
- if (!is_java_array)
- string_append (tname, "<");
- /* get size of template parameter list */
- if (!get_count (mangled, &r))
- {
- return (0);
- }
- if (!is_type)
- {
- /* Create an array for saving the template argument values. */
- work->tmpl_argvec = XNEWVEC (char *, r);
- work->ntmpl_args = r;
- for (i = 0; i < r; i++)
- work->tmpl_argvec[i] = 0;
- }
- for (i = 0; i < r; i++)
- {
- if (need_comma)
- {
- string_append (tname, ", ");
- }
- /* Z for type parameters */
- if (**mangled == 'Z')
- {
- (*mangled)++;
- /* temp is initialized in do_type */
- success = do_type (work, mangled, &temp);
- if (success)
- {
- string_appends (tname, &temp);
-
- if (!is_type)
- {
- /* Save the template argument. */
- int len = temp.p - temp.b;
- work->tmpl_argvec[i] = XNEWVEC (char, len + 1);
- memcpy (work->tmpl_argvec[i], temp.b, len);
- work->tmpl_argvec[i][len] = '\0';
- }
- }
- string_delete(&temp);
- if (!success)
- {
- break;
- }
- }
- /* z for template parameters */
- else if (**mangled == 'z')
- {
- int r2;
- (*mangled)++;
- success = demangle_template_template_parm (work, mangled, tname);
-
- if (success
- && (r2 = consume_count (mangled)) > 0
- && (int) strlen (*mangled) >= r2)
- {
- string_append (tname, " ");
- string_appendn (tname, *mangled, r2);
- if (!is_type)
- {
- /* Save the template argument. */
- int len = r2;
- work->tmpl_argvec[i] = XNEWVEC (char, len + 1);
- memcpy (work->tmpl_argvec[i], *mangled, len);
- work->tmpl_argvec[i][len] = '\0';
- }
- *mangled += r2;
- }
- if (!success)
- {
- break;
- }
- }
- else
- {
- string param;
- string* s;
-
- /* otherwise, value parameter */
-
- /* temp is initialized in do_type */
- success = do_type (work, mangled, &temp);
- string_delete(&temp);
- if (!success)
- break;
-
- if (!is_type)
- {
- s = &param;
- string_init (s);
- }
- else
- s = tname;
-
- success = demangle_template_value_parm (work, mangled, s,
- (type_kind_t) success);
-
- if (!success)
- {
- if (!is_type)
- string_delete (s);
- success = 0;
- break;
- }
-
- if (!is_type)
- {
- int len = s->p - s->b;
- work->tmpl_argvec[i] = XNEWVEC (char, len + 1);
- memcpy (work->tmpl_argvec[i], s->b, len);
- work->tmpl_argvec[i][len] = '\0';
-
- string_appends (tname, s);
- string_delete (s);
- }
- }
- need_comma = 1;
- }
- if (is_java_array)
- {
- string_append (tname, "[]");
- }
- else
- {
- if (tname->p[-1] == '>')
- string_append (tname, " ");
- string_append (tname, ">");
- }
-
- if (is_type && remember)
- {
- const int bindex = register_Btype (work);
- remember_Btype (work, tname->b, LEN_STRING (tname), bindex);
- }
-
- /*
- if (work -> static_type)
- {
- string_append (declp, *mangled + 1);
- *mangled += strlen (*mangled);
- success = 1;
- }
- else
- {
- success = demangle_args (work, mangled, declp);
- }
- }
- */
- return (success);
-}
-
-static int
-arm_pt (struct work_stuff *work, const char *mangled,
- int n, const char **anchor, const char **args)
-{
- /* Check if ARM template with "__pt__" in it ("parameterized type") */
- /* Allow HP also here, because HP's cfront compiler follows ARM to some extent */
- if ((ARM_DEMANGLING || HP_DEMANGLING) && (*anchor = strstr (mangled, "__pt__")))
- {
- int len;
- *args = *anchor + 6;
- len = consume_count (args);
- if (len == -1)
- return 0;
- if (*args + len == mangled + n && **args == '_')
- {
- ++*args;
- return 1;
- }
- }
- if (AUTO_DEMANGLING || EDG_DEMANGLING)
- {
- if ((*anchor = strstr (mangled, "__tm__"))
- || (*anchor = strstr (mangled, "__ps__"))
- || (*anchor = strstr (mangled, "__pt__")))
- {
- int len;
- *args = *anchor + 6;
- len = consume_count (args);
- if (len == -1)
- return 0;
- if (*args + len == mangled + n && **args == '_')
- {
- ++*args;
- return 1;
- }
- }
- else if ((*anchor = strstr (mangled, "__S")))
- {
- int len;
- *args = *anchor + 3;
- len = consume_count (args);
- if (len == -1)
- return 0;
- if (*args + len == mangled + n && **args == '_')
- {
- ++*args;
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-static void
-demangle_arm_hp_template (struct work_stuff *work, const char **mangled,
- int n, string *declp)
-{
- const char *p;
- const char *args;
- const char *e = *mangled + n;
- string arg;
-
- /* Check for HP aCC template spec: classXt1t2 where t1, t2 are
- template args */
- if (HP_DEMANGLING && ((*mangled)[n] == 'X'))
- {
- char *start_spec_args = NULL;
- int hold_options;
-
- /* First check for and omit template specialization pseudo-arguments,
- such as in "Spec<#1,#1.*>" */
- start_spec_args = strchr (*mangled, '<');
- if (start_spec_args && (start_spec_args - *mangled < n))
- string_appendn (declp, *mangled, start_spec_args - *mangled);
- else
- string_appendn (declp, *mangled, n);
- (*mangled) += n + 1;
- string_init (&arg);
- if (work->temp_start == -1) /* non-recursive call */
- work->temp_start = declp->p - declp->b;
-
- /* We want to unconditionally demangle parameter types in
- template parameters. */
- hold_options = work->options;
- work->options |= DMGL_PARAMS;
-
- string_append (declp, "<");
- while (1)
- {
- string_delete (&arg);
- switch (**mangled)
- {
- case 'T':
- /* 'T' signals a type parameter */
- (*mangled)++;
- if (!do_type (work, mangled, &arg))
- goto hpacc_template_args_done;
- break;
-
- case 'U':
- case 'S':
- /* 'U' or 'S' signals an integral value */
- if (!do_hpacc_template_const_value (work, mangled, &arg))
- goto hpacc_template_args_done;
- break;
-
- case 'A':
- /* 'A' signals a named constant expression (literal) */
- if (!do_hpacc_template_literal (work, mangled, &arg))
- goto hpacc_template_args_done;
- break;
-
- default:
- /* Today, 1997-09-03, we have only the above types
- of template parameters */
- /* FIXME: maybe this should fail and return null */
- goto hpacc_template_args_done;
- }
- string_appends (declp, &arg);
- /* Check if we're at the end of template args.
- 0 if at end of static member of template class,
- _ if done with template args for a function */
- if ((**mangled == '\000') || (**mangled == '_'))
- break;
- else
- string_append (declp, ",");
- }
- hpacc_template_args_done:
- string_append (declp, ">");
- string_delete (&arg);
- if (**mangled == '_')
- (*mangled)++;
- work->options = hold_options;
- return;
- }
- /* ARM template? (Also handles HP cfront extensions) */
- else if (arm_pt (work, *mangled, n, &p, &args))
- {
- int hold_options;
- string type_str;
-
- string_init (&arg);
- string_appendn (declp, *mangled, p - *mangled);
- if (work->temp_start == -1) /* non-recursive call */
- work->temp_start = declp->p - declp->b;
-
- /* We want to unconditionally demangle parameter types in
- template parameters. */
- hold_options = work->options;
- work->options |= DMGL_PARAMS;
-
- string_append (declp, "<");
- /* should do error checking here */
- while (args < e) {
- string_delete (&arg);
-
- /* Check for type or literal here */
- switch (*args)
- {
- /* HP cfront extensions to ARM for template args */
- /* spec: Xt1Lv1 where t1 is a type, v1 is a literal value */
- /* FIXME: We handle only numeric literals for HP cfront */
- case 'X':
- /* A typed constant value follows */
- args++;
- if (!do_type (work, &args, &type_str))
- goto cfront_template_args_done;
- string_append (&arg, "(");
- string_appends (&arg, &type_str);
- string_delete (&type_str);
- string_append (&arg, ")");
- if (*args != 'L')
- goto cfront_template_args_done;
- args++;
- /* Now snarf a literal value following 'L' */
- if (!snarf_numeric_literal (&args, &arg))
- goto cfront_template_args_done;
- break;
-
- case 'L':
- /* Snarf a literal following 'L' */
- args++;
- if (!snarf_numeric_literal (&args, &arg))
- goto cfront_template_args_done;
- break;
- default:
- /* Not handling other HP cfront stuff */
- {
- const char* old_args = args;
- if (!do_type (work, &args, &arg))
- goto cfront_template_args_done;
-
- /* Fail if we didn't make any progress: prevent infinite loop. */
- if (args == old_args)
- {
- work->options = hold_options;
- return;
- }
- }
- }
- string_appends (declp, &arg);
- string_append (declp, ",");
- }
- cfront_template_args_done:
- string_delete (&arg);
- if (args >= e)
- --declp->p; /* remove extra comma */
- string_append (declp, ">");
- work->options = hold_options;
- }
- else if (n>10 && strncmp (*mangled, "_GLOBAL_", 8) == 0
- && (*mangled)[9] == 'N'
- && (*mangled)[8] == (*mangled)[10]
- && strchr (cplus_markers, (*mangled)[8]))
- {
- /* A member of the anonymous namespace. */
- string_append (declp, "{anonymous}");
- }
- else
- {
- if (work->temp_start == -1) /* non-recursive call only */
- work->temp_start = 0; /* disable in recursive calls */
- string_appendn (declp, *mangled, n);
- }
- *mangled += n;
-}
-
-/* Extract a class name, possibly a template with arguments, from the
- mangled string; qualifiers, local class indicators, etc. have
- already been dealt with */
-
-static int
-demangle_class_name (struct work_stuff *work, const char **mangled,
- string *declp)
-{
- int n;
- int success = 0;
-
- n = consume_count (mangled);
- if (n == -1)
- return 0;
- if ((int) strlen (*mangled) >= n)
- {
- demangle_arm_hp_template (work, mangled, n, declp);
- success = 1;
- }
-
- return (success);
-}
-
-/*
-
-LOCAL FUNCTION
-
- demangle_class -- demangle a mangled class sequence
-
-SYNOPSIS
-
- static int
- demangle_class (struct work_stuff *work, const char **mangled,
- strint *declp)
-
-DESCRIPTION
-
- DECLP points to the buffer into which demangling is being done.
-
- *MANGLED points to the current token to be demangled. On input,
- it points to a mangled class (I.E. "3foo", "13verylongclass", etc.)
- On exit, it points to the next token after the mangled class on
- success, or the first unconsumed token on failure.
-
- If the CONSTRUCTOR or DESTRUCTOR flags are set in WORK, then
- we are demangling a constructor or destructor. In this case
- we prepend "class::class" or "class::~class" to DECLP.
-
- Otherwise, we prepend "class::" to the current DECLP.
-
- Reset the constructor/destructor flags once they have been
- "consumed". This allows demangle_class to be called later during
- the same demangling, to do normal class demangling.
-
- Returns 1 if demangling is successful, 0 otherwise.
-
-*/
-
-static int
-demangle_class (struct work_stuff *work, const char **mangled, string *declp)
-{
- int success = 0;
- int btype;
- string class_name;
- char *save_class_name_end = 0;
-
- string_init (&class_name);
- btype = register_Btype (work);
- if (demangle_class_name (work, mangled, &class_name))
- {
- save_class_name_end = class_name.p;
- if ((work->constructor & 1) || (work->destructor & 1))
- {
- /* adjust so we don't include template args */
- if (work->temp_start && (work->temp_start != -1))
- {
- class_name.p = class_name.b + work->temp_start;
- }
- string_prepends (declp, &class_name);
- if (work -> destructor & 1)
- {
- string_prepend (declp, "~");
- work -> destructor -= 1;
- }
- else
- {
- work -> constructor -= 1;
- }
- }
- class_name.p = save_class_name_end;
- remember_Ktype (work, class_name.b, LEN_STRING(&class_name));
- remember_Btype (work, class_name.b, LEN_STRING(&class_name), btype);
- string_prepend (declp, SCOPE_STRING (work));
- string_prepends (declp, &class_name);
- success = 1;
- }
- string_delete (&class_name);
- return (success);
-}
-
-
-/* Called when there's a "__" in the mangled name, with `scan' pointing to
- the rightmost guess.
-
- Find the correct "__"-sequence where the function name ends and the
- signature starts, which is ambiguous with GNU mangling.
- Call demangle_signature here, so we can make sure we found the right
- one; *mangled will be consumed so caller will not make further calls to
- demangle_signature. */
-
-static int
-iterate_demangle_function (struct work_stuff *work, const char **mangled,
- string *declp, const char *scan)
-{
- const char *mangle_init = *mangled;
- int success = 0;
- string decl_init;
- struct work_stuff work_init;
-
- if (*(scan + 2) == '\0')
- return 0;
-
- /* Do not iterate for some demangling modes, or if there's only one
- "__"-sequence. This is the normal case. */
- if (ARM_DEMANGLING || LUCID_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING
- || strstr (scan + 2, "__") == NULL)
- return demangle_function_name (work, mangled, declp, scan);
-
- /* Save state so we can restart if the guess at the correct "__" was
- wrong. */
- string_init (&decl_init);
- string_appends (&decl_init, declp);
- memset (&work_init, 0, sizeof work_init);
- work_stuff_copy_to_from (&work_init, work);
-
- /* Iterate over occurrences of __, allowing names and types to have a
- "__" sequence in them. We must start with the first (not the last)
- occurrence, since "__" most often occur between independent mangled
- parts, hence starting at the last occurence inside a signature
- might get us a "successful" demangling of the signature. */
-
- while (scan[2])
- {
- if (demangle_function_name (work, mangled, declp, scan))
- {
- success = demangle_signature (work, mangled, declp);
- if (success)
- break;
- }
-
- /* Reset demangle state for the next round. */
- *mangled = mangle_init;
- string_clear (declp);
- string_appends (declp, &decl_init);
- work_stuff_copy_to_from (work, &work_init);
-
- /* Leave this underscore-sequence. */
- scan += 2;
-
- /* Scan for the next "__" sequence. */
- while (*scan && (scan[0] != '_' || scan[1] != '_'))
- scan++;
-
- /* Move to last "__" in this sequence. */
- while (*scan && *scan == '_')
- scan++;
- scan -= 2;
- }
-
- /* Delete saved state. */
- delete_work_stuff (&work_init);
- string_delete (&decl_init);
-
- return success;
-}
-
-/*
-
-LOCAL FUNCTION
-
- demangle_prefix -- consume the mangled name prefix and find signature
-
-SYNOPSIS
-
- static int
- demangle_prefix (struct work_stuff *work, const char **mangled,
- string *declp);
-
-DESCRIPTION
-
- Consume and demangle the prefix of the mangled name.
- While processing the function name root, arrange to call
- demangle_signature if the root is ambiguous.
-
- DECLP points to the string buffer into which demangled output is
- placed. On entry, the buffer is empty. On exit it contains
- the root function name, the demangled operator name, or in some
- special cases either nothing or the completely demangled result.
-
- MANGLED points to the current pointer into the mangled name. As each
- token of the mangled name is consumed, it is updated. Upon entry
- the current mangled name pointer points to the first character of
- the mangled name. Upon exit, it should point to the first character
- of the signature if demangling was successful, or to the first
- unconsumed character if demangling of the prefix was unsuccessful.
-
- Returns 1 on success, 0 otherwise.
- */
-
-static int
-demangle_prefix (struct work_stuff *work, const char **mangled,
- string *declp)
-{
- int success = 1;
- const char *scan;
- int i;
-
- if (strlen(*mangled) > 6
- && (strncmp(*mangled, "_imp__", 6) == 0
- || strncmp(*mangled, "__imp_", 6) == 0))
- {
- /* it's a symbol imported from a PE dynamic library. Check for both
- new style prefix _imp__ and legacy __imp_ used by older versions
- of dlltool. */
- (*mangled) += 6;
- work->dllimported = 1;
- }
- else if (strlen(*mangled) >= 11 && strncmp(*mangled, "_GLOBAL_", 8) == 0)
- {
- char *marker = strchr (cplus_markers, (*mangled)[8]);
- if (marker != NULL && *marker == (*mangled)[10])
- {
- if ((*mangled)[9] == 'D')
- {
- /* it's a GNU global destructor to be executed at program exit */
- (*mangled) += 11;
- work->destructor = 2;
- if (gnu_special (work, mangled, declp))
- return success;
- }
- else if ((*mangled)[9] == 'I')
- {
- /* it's a GNU global constructor to be executed at program init */
- (*mangled) += 11;
- work->constructor = 2;
- if (gnu_special (work, mangled, declp))
- return success;
- }
- }
- }
- else if ((ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) && strncmp(*mangled, "__std__", 7) == 0)
- {
- /* it's a ARM global destructor to be executed at program exit */
- (*mangled) += 7;
- work->destructor = 2;
- }
- else if ((ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) && strncmp(*mangled, "__sti__", 7) == 0)
- {
- /* it's a ARM global constructor to be executed at program initial */
- (*mangled) += 7;
- work->constructor = 2;
- }
-
- /* This block of code is a reduction in strength time optimization
- of:
- scan = strstr (*mangled, "__"); */
-
- {
- scan = *mangled;
-
- do {
- scan = strchr (scan, '_');
- } while (scan != NULL && *++scan != '_');
-
- if (scan != NULL) --scan;
- }
-
- if (scan != NULL)
- {
- /* We found a sequence of two or more '_', ensure that we start at
- the last pair in the sequence. */
- i = strspn (scan, "_");
- if (i > 2)
- {
- scan += (i - 2);
- }
- }
-
- if (scan == NULL)
- {
- success = 0;
- }
- else if (work -> static_type)
- {
- if (!ISDIGIT ((unsigned char)scan[0]) && (scan[0] != 't'))
- {
- success = 0;
- }
- }
- else if ((scan == *mangled)
- && (ISDIGIT ((unsigned char)scan[2]) || (scan[2] == 'Q')
- || (scan[2] == 't') || (scan[2] == 'K') || (scan[2] == 'H')))
- {
- /* The ARM says nothing about the mangling of local variables.
- But cfront mangles local variables by prepending __<nesting_level>
- to them. As an extension to ARM demangling we handle this case. */
- if ((LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING)
- && ISDIGIT ((unsigned char)scan[2]))
- {
- *mangled = scan + 2;
- consume_count (mangled);
- string_append (declp, *mangled);
- *mangled += strlen (*mangled);
- success = 1;
- }
- else
- {
- /* A GNU style constructor starts with __[0-9Qt]. But cfront uses
- names like __Q2_3foo3bar for nested type names. So don't accept
- this style of constructor for cfront demangling. A GNU
- style member-template constructor starts with 'H'. */
- if (!(LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING))
- work -> constructor += 1;
- *mangled = scan + 2;
- }
- }
- else if (ARM_DEMANGLING && scan[2] == 'p' && scan[3] == 't')
- {
- /* Cfront-style parameterized type. Handled later as a signature. */
- success = 1;
-
- /* ARM template? */
- demangle_arm_hp_template (work, mangled, strlen (*mangled), declp);
- }
- else if (EDG_DEMANGLING && ((scan[2] == 't' && scan[3] == 'm')
- || (scan[2] == 'p' && scan[3] == 's')
- || (scan[2] == 'p' && scan[3] == 't')))
- {
- /* EDG-style parameterized type. Handled later as a signature. */
- success = 1;
-
- /* EDG template? */
- demangle_arm_hp_template (work, mangled, strlen (*mangled), declp);
- }
- else if ((scan == *mangled) && !ISDIGIT ((unsigned char)scan[2])
- && (scan[2] != 't'))
- {
- /* Mangled name starts with "__". Skip over any leading '_' characters,
- then find the next "__" that separates the prefix from the signature.
- */
- if (!(ARM_DEMANGLING || LUCID_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING)
- || (arm_special (mangled, declp) == 0))
- {
- while (*scan == '_')
- {
- scan++;
- }
- if ((scan = strstr (scan, "__")) == NULL || (*(scan + 2) == '\0'))
- {
- /* No separator (I.E. "__not_mangled"), or empty signature
- (I.E. "__not_mangled_either__") */
- success = 0;
- }
- else
- return iterate_demangle_function (work, mangled, declp, scan);
- }
- }
- else if (*(scan + 2) != '\0')
- {
- /* Mangled name does not start with "__" but does have one somewhere
- in there with non empty stuff after it. Looks like a global
- function name. Iterate over all "__":s until the right
- one is found. */
- return iterate_demangle_function (work, mangled, declp, scan);
- }
- else
- {
- /* Doesn't look like a mangled name */
- success = 0;
- }
-
- if (!success && (work->constructor == 2 || work->destructor == 2))
- {
- string_append (declp, *mangled);
- *mangled += strlen (*mangled);
- success = 1;
- }
- return (success);
-}
-
-/*
-
-LOCAL FUNCTION
-
- gnu_special -- special handling of gnu mangled strings
-
-SYNOPSIS
-
- static int
- gnu_special (struct work_stuff *work, const char **mangled,
- string *declp);
-
-
-DESCRIPTION
-
- Process some special GNU style mangling forms that don't fit
- the normal pattern. For example:
-
- _$_3foo (destructor for class foo)
- _vt$foo (foo virtual table)
- _vt$foo$bar (foo::bar virtual table)
- __vt_foo (foo virtual table, new style with thunks)
- _3foo$varname (static data member)
- _Q22rs2tu$vw (static data member)
- __t6vector1Zii (constructor with template)
- __thunk_4__$_7ostream (virtual function thunk)
- */
-
-static int
-gnu_special (struct work_stuff *work, const char **mangled, string *declp)
-{
- int n;
- int success = 1;
- const char *p;
-
- if ((*mangled)[0] == '_' && (*mangled)[1] != '\0'
- && strchr (cplus_markers, (*mangled)[1]) != NULL
- && (*mangled)[2] == '_')
- {
- /* Found a GNU style destructor, get past "_<CPLUS_MARKER>_" */
- (*mangled) += 3;
- work -> destructor += 1;
- }
- else if ((*mangled)[0] == '_'
- && (((*mangled)[1] == '_'
- && (*mangled)[2] == 'v'
- && (*mangled)[3] == 't'
- && (*mangled)[4] == '_')
- || ((*mangled)[1] == 'v'
- && (*mangled)[2] == 't' && (*mangled)[3] != '\0'
- && strchr (cplus_markers, (*mangled)[3]) != NULL)))
- {
- /* Found a GNU style virtual table, get past "_vt<CPLUS_MARKER>"
- and create the decl. Note that we consume the entire mangled
- input string, which means that demangle_signature has no work
- to do. */
- if ((*mangled)[2] == 'v')
- (*mangled) += 5; /* New style, with thunks: "__vt_" */
- else
- (*mangled) += 4; /* Old style, no thunks: "_vt<CPLUS_MARKER>" */
- while (**mangled != '\0')
- {
- switch (**mangled)
- {
- case 'Q':
- case 'K':
- success = demangle_qualified (work, mangled, declp, 0, 1);
- break;
- case 't':
- success = demangle_template (work, mangled, declp, 0, 1,
- 1);
- break;
- default:
- if (ISDIGIT((unsigned char)*mangled[0]))
- {
- n = consume_count(mangled);
- /* We may be seeing a too-large size, or else a
- ".<digits>" indicating a static local symbol. In
- any case, declare victory and move on; *don't* try
- to use n to allocate. */
- if (n > (int) strlen (*mangled))
- {
- success = 1;
- break;
- }
- else if (n == -1)
- {
- success = 0;
- break;
- }
- }
- else
- {
- n = strcspn (*mangled, cplus_markers);
- }
- string_appendn (declp, *mangled, n);
- (*mangled) += n;
- }
-
- p = strpbrk (*mangled, cplus_markers);
- if (success && ((p == NULL) || (p == *mangled)))
- {
- if (p != NULL)
- {
- string_append (declp, SCOPE_STRING (work));
- (*mangled)++;
- }
- }
- else
- {
- success = 0;
- break;
- }
- }
- if (success)
- string_append (declp, " virtual table");
- }
- else if ((*mangled)[0] == '_'
- && (strchr("0123456789Qt", (*mangled)[1]) != NULL)
- && (p = strpbrk (*mangled, cplus_markers)) != NULL)
- {
- /* static data member, "_3foo$varname" for example */
- (*mangled)++;
- switch (**mangled)
- {
- case 'Q':
- case 'K':
- success = demangle_qualified (work, mangled, declp, 0, 1);
- break;
- case 't':
- success = demangle_template (work, mangled, declp, 0, 1, 1);
- break;
- default:
- n = consume_count (mangled);
- if (n < 0 || n > (long) strlen (*mangled))
- {
- success = 0;
- break;
- }
-
- if (n > 10 && strncmp (*mangled, "_GLOBAL_", 8) == 0
- && (*mangled)[9] == 'N'
- && (*mangled)[8] == (*mangled)[10]
- && strchr (cplus_markers, (*mangled)[8]))
- {
- /* A member of the anonymous namespace. There's information
- about what identifier or filename it was keyed to, but
- it's just there to make the mangled name unique; we just
- step over it. */
- string_append (declp, "{anonymous}");
- (*mangled) += n;
-
- /* Now p points to the marker before the N, so we need to
- update it to the first marker after what we consumed. */
- p = strpbrk (*mangled, cplus_markers);
- break;
- }
-
- string_appendn (declp, *mangled, n);
- (*mangled) += n;
- }
- if (success && (p == *mangled))
- {
- /* Consumed everything up to the cplus_marker, append the
- variable name. */
- (*mangled)++;
- string_append (declp, SCOPE_STRING (work));
- n = strlen (*mangled);
- string_appendn (declp, *mangled, n);
- (*mangled) += n;
- }
- else
- {
- success = 0;
- }
- }
- else if (strncmp (*mangled, "__thunk_", 8) == 0)
- {
- int delta;
-
- (*mangled) += 8;
- delta = consume_count (mangled);
- if (delta == -1)
- success = 0;
- else if (**mangled != '_')
- success = 0;
- else
- {
- char *method = internal_cplus_demangle (work, ++*mangled);
-
- if (method)
- {
- char buf[50];
- sprintf (buf, "virtual function thunk (delta:%d) for ", -delta);
- string_append (declp, buf);
- string_append (declp, method);
- free (method);
- n = strlen (*mangled);
- (*mangled) += n;
- }
- else
- {
- success = 0;
- }
- }
- }
- else if (strncmp (*mangled, "__t", 3) == 0
- && ((*mangled)[3] == 'i' || (*mangled)[3] == 'f'))
- {
- p = (*mangled)[3] == 'i' ? " type_info node" : " type_info function";
- (*mangled) += 4;
- switch (**mangled)
- {
- case 'Q':
- case 'K':
- success = demangle_qualified (work, mangled, declp, 0, 1);
- break;
- case 't':
- success = demangle_template (work, mangled, declp, 0, 1, 1);
- break;
- default:
- success = do_type (work, mangled, declp);
- break;
- }
- if (success && **mangled != '\0')
- success = 0;
- if (success)
- string_append (declp, p);
- }
- else
- {
- success = 0;
- }
- return (success);
-}
-
-static void
-recursively_demangle(struct work_stuff *work, const char **mangled,
- string *result, int namelength)
-{
- char * recurse = (char *)NULL;
- char * recurse_dem = (char *)NULL;
-
- recurse = XNEWVEC (char, namelength + 1);
- memcpy (recurse, *mangled, namelength);
- recurse[namelength] = '\000';
-
- recurse_dem = cplus_demangle (recurse, work->options);
-
- if (recurse_dem)
- {
- string_append (result, recurse_dem);
- free (recurse_dem);
- }
- else
- {
- string_appendn (result, *mangled, namelength);
- }
- free (recurse);
- *mangled += namelength;
-}
-
-/*
-
-LOCAL FUNCTION
-
- arm_special -- special handling of ARM/lucid mangled strings
-
-SYNOPSIS
-
- static int
- arm_special (const char **mangled,
- string *declp);
-
-
-DESCRIPTION
-
- Process some special ARM style mangling forms that don't fit
- the normal pattern. For example:
-
- __vtbl__3foo (foo virtual table)
- __vtbl__3foo__3bar (bar::foo virtual table)
-
- */
-
-static int
-arm_special (const char **mangled, string *declp)
-{
- int n;
- int success = 1;
- const char *scan;
-
- if (strncmp (*mangled, ARM_VTABLE_STRING, ARM_VTABLE_STRLEN) == 0)
- {
- /* Found a ARM style virtual table, get past ARM_VTABLE_STRING
- and create the decl. Note that we consume the entire mangled
- input string, which means that demangle_signature has no work
- to do. */
- scan = *mangled + ARM_VTABLE_STRLEN;
- while (*scan != '\0') /* first check it can be demangled */
- {
- n = consume_count (&scan);
- if (n == -1)
- {
- return (0); /* no good */
- }
- scan += n;
- if (scan[0] == '_' && scan[1] == '_')
- {
- scan += 2;
- }
- }
- (*mangled) += ARM_VTABLE_STRLEN;
- while (**mangled != '\0')
- {
- n = consume_count (mangled);
- if (n == -1
- || n > (long) strlen (*mangled))
- return 0;
- string_prependn (declp, *mangled, n);
- (*mangled) += n;
- if ((*mangled)[0] == '_' && (*mangled)[1] == '_')
- {
- string_prepend (declp, "::");
- (*mangled) += 2;
- }
- }
- string_append (declp, " virtual table");
- }
- else
- {
- success = 0;
- }
- return (success);
-}
-
-/*
-
-LOCAL FUNCTION
-
- demangle_qualified -- demangle 'Q' qualified name strings
-
-SYNOPSIS
-
- static int
- demangle_qualified (struct work_stuff *, const char *mangled,
- string *result, int isfuncname, int append);
-
-DESCRIPTION
-
- Demangle a qualified name, such as "Q25Outer5Inner" which is
- the mangled form of "Outer::Inner". The demangled output is
- prepended or appended to the result string according to the
- state of the append flag.
-
- If isfuncname is nonzero, then the qualified name we are building
- is going to be used as a member function name, so if it is a
- constructor or destructor function, append an appropriate
- constructor or destructor name. I.E. for the above example,
- the result for use as a constructor is "Outer::Inner::Inner"
- and the result for use as a destructor is "Outer::Inner::~Inner".
-
-BUGS
-
- Numeric conversion is ASCII dependent (FIXME).
-
- */
-
-static int
-demangle_qualified (struct work_stuff *work, const char **mangled,
- string *result, int isfuncname, int append)
-{
- int qualifiers = 0;
- int success = 1;
- char num[2];
- string temp;
- string last_name;
- int bindex = register_Btype (work);
-
- /* We only make use of ISFUNCNAME if the entity is a constructor or
- destructor. */
- isfuncname = (isfuncname
- && ((work->constructor & 1) || (work->destructor & 1)));
-
- string_init (&temp);
- string_init (&last_name);
-
- if ((*mangled)[0] == 'K')
- {
- /* Squangling qualified name reuse */
- int idx;
- (*mangled)++;
- idx = consume_count_with_underscores (mangled);
- if (idx == -1 || idx >= work -> numk)
- success = 0;
- else
- string_append (&temp, work -> ktypevec[idx]);
- }
- else
- switch ((*mangled)[1])
- {
- case '_':
- /* GNU mangled name with more than 9 classes. The count is preceded
- by an underscore (to distinguish it from the <= 9 case) and followed
- by an underscore. */
- (*mangled)++;
- qualifiers = consume_count_with_underscores (mangled);
- if (qualifiers == -1)
- success = 0;
- break;
-
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- /* The count is in a single digit. */
- num[0] = (*mangled)[1];
- num[1] = '\0';
- qualifiers = atoi (num);
-
- /* If there is an underscore after the digit, skip it. This is
- said to be for ARM-qualified names, but the ARM makes no
- mention of such an underscore. Perhaps cfront uses one. */
- if ((*mangled)[2] == '_')
- {
- (*mangled)++;
- }
- (*mangled) += 2;
- break;
-
- case '0':
- default:
- success = 0;
- }
-
- if (!success)
- return success;
-
- /* Pick off the names and collect them in the temp buffer in the order
- in which they are found, separated by '::'. */
-
- while (qualifiers-- > 0)
- {
- int remember_K = 1;
- string_clear (&last_name);
-
- if (*mangled[0] == '_')
- (*mangled)++;
-
- if (*mangled[0] == 't')
- {
- /* Here we always append to TEMP since we will want to use
- the template name without the template parameters as a
- constructor or destructor name. The appropriate
- (parameter-less) value is returned by demangle_template
- in LAST_NAME. We do not remember the template type here,
- in order to match the G++ mangling algorithm. */
- success = demangle_template(work, mangled, &temp,
- &last_name, 1, 0);
- if (!success)
- break;
- }
- else if (*mangled[0] == 'K')
- {
- int idx;
- (*mangled)++;
- idx = consume_count_with_underscores (mangled);
- if (idx == -1 || idx >= work->numk)
- success = 0;
- else
- string_append (&temp, work->ktypevec[idx]);
- remember_K = 0;
-
- if (!success) break;
- }
- else
- {
- if (EDG_DEMANGLING)
- {
- int namelength;
- /* Now recursively demangle the qualifier
- * This is necessary to deal with templates in
- * mangling styles like EDG */
- namelength = consume_count (mangled);
- if (namelength == -1)
- {
- success = 0;
- break;
- }
- recursively_demangle(work, mangled, &temp, namelength);
- }
- else
- {
- string_delete (&last_name);
- success = do_type (work, mangled, &last_name);
- if (!success)
- break;
- string_appends (&temp, &last_name);
- }
- }
-
- if (remember_K)
- remember_Ktype (work, temp.b, LEN_STRING (&temp));
-
- if (qualifiers > 0)
- string_append (&temp, SCOPE_STRING (work));
- }
-
- remember_Btype (work, temp.b, LEN_STRING (&temp), bindex);
-
- /* If we are using the result as a function name, we need to append
- the appropriate '::' separated constructor or destructor name.
- We do this here because this is the most convenient place, where
- we already have a pointer to the name and the length of the name. */
-
- if (isfuncname)
- {
- string_append (&temp, SCOPE_STRING (work));
- if (work -> destructor & 1)
- string_append (&temp, "~");
- string_appends (&temp, &last_name);
- }
-
- /* Now either prepend the temp buffer to the result, or append it,
- depending upon the state of the append flag. */
-
- if (append)
- string_appends (result, &temp);
- else
- {
- if (!STRING_EMPTY (result))
- string_append (&temp, SCOPE_STRING (work));
- string_prepends (result, &temp);
- }
-
- string_delete (&last_name);
- string_delete (&temp);
- return (success);
-}
-
-/*
-
-LOCAL FUNCTION
-
- get_count -- convert an ascii count to integer, consuming tokens
-
-SYNOPSIS
-
- static int
- get_count (const char **type, int *count)
-
-DESCRIPTION
-
- Assume that *type points at a count in a mangled name; set
- *count to its value, and set *type to the next character after
- the count. There are some weird rules in effect here.
-
- If *type does not point at a string of digits, return zero.
-
- If *type points at a string of digits followed by an
- underscore, set *count to their value as an integer, advance
- *type to point *after the underscore, and return 1.
-
- If *type points at a string of digits not followed by an
- underscore, consume only the first digit. Set *count to its
- value as an integer, leave *type pointing after that digit,
- and return 1.
-
- The excuse for this odd behavior: in the ARM and HP demangling
- styles, a type can be followed by a repeat count of the form
- `Nxy', where:
-
- `x' is a single digit specifying how many additional copies
- of the type to append to the argument list, and
-
- `y' is one or more digits, specifying the zero-based index of
- the first repeated argument in the list. Yes, as you're
- unmangling the name you can figure this out yourself, but
- it's there anyway.
-
- So, for example, in `bar__3fooFPiN51', the first argument is a
- pointer to an integer (`Pi'), and then the next five arguments
- are the same (`N5'), and the first repeat is the function's
- second argument (`1').
-*/
-
-static int
-get_count (const char **type, int *count)
-{
- const char *p;
- int n;
-
- if (!ISDIGIT ((unsigned char)**type))
- return (0);
- else
- {
- *count = **type - '0';
- (*type)++;
- if (ISDIGIT ((unsigned char)**type))
- {
- p = *type;
- n = *count;
- do
- {
- n *= 10;
- n += *p - '0';
- p++;
- }
- while (ISDIGIT ((unsigned char)*p));
- if (*p == '_')
- {
- *type = p + 1;
- *count = n;
- }
- }
- }
- return (1);
-}
-
-/* RESULT will be initialised here; it will be freed on failure. The
- value returned is really a type_kind_t. */
-
-static int
-do_type (struct work_stuff *work, const char **mangled, string *result)
-{
- int n;
- int i;
- int is_proctypevec;
- int done;
- int success;
- string decl;
- const char *remembered_type;
- int type_quals;
- type_kind_t tk = tk_none;
-
- string_init (&decl);
- string_init (result);
-
- done = 0;
- success = 1;
- is_proctypevec = 0;
- while (success && !done)
- {
- int member;
- switch (**mangled)
- {
-
- /* A pointer type */
- case 'P':
- case 'p':
- (*mangled)++;
- if (! (work -> options & DMGL_JAVA))
- string_prepend (&decl, "*");
- if (tk == tk_none)
- tk = tk_pointer;
- break;
-
- /* A reference type */
- case 'R':
- (*mangled)++;
- string_prepend (&decl, "&");
- if (tk == tk_none)
- tk = tk_reference;
- break;
-
- /* An rvalue reference type */
- case 'O':
- (*mangled)++;
- string_prepend (&decl, "&&");
- if (tk == tk_none)
- tk = tk_rvalue_reference;
- break;
-
- /* An array */
- case 'A':
- {
- ++(*mangled);
- if (!STRING_EMPTY (&decl)
- && (decl.b[0] == '*' || decl.b[0] == '&'))
- {
- string_prepend (&decl, "(");
- string_append (&decl, ")");
- }
- string_append (&decl, "[");
- if (**mangled != '_')
- success = demangle_template_value_parm (work, mangled, &decl,
- tk_integral);
- if (**mangled == '_')
- ++(*mangled);
- string_append (&decl, "]");
- break;
- }
-
- /* A back reference to a previously seen type */
- case 'T':
- (*mangled)++;
- if (!get_count (mangled, &n) || n < 0 || n >= work -> ntypes)
- {
- success = 0;
- }
- else
- for (i = 0; i < work->nproctypes; i++)
- if (work -> proctypevec [i] == n)
- success = 0;
-
- if (success)
- {
- is_proctypevec = 1;
- push_processed_type (work, n);
- remembered_type = work->typevec[n];
- mangled = &remembered_type;
- }
- break;
-
- /* A function */
- case 'F':
- (*mangled)++;
- if (!STRING_EMPTY (&decl)
- && (decl.b[0] == '*' || decl.b[0] == '&'))
- {
- string_prepend (&decl, "(");
- string_append (&decl, ")");
- }
- /* After picking off the function args, we expect to either find the
- function return type (preceded by an '_') or the end of the
- string. */
- if (!demangle_nested_args (work, mangled, &decl)
- || (**mangled != '_' && **mangled != '\0'))
- {
- success = 0;
- break;
- }
- if (success && (**mangled == '_'))
- (*mangled)++;
- break;
-
- case 'M':
- {
- type_quals = TYPE_UNQUALIFIED;
-
- member = **mangled == 'M';
- (*mangled)++;
-
- string_append (&decl, ")");
-
- /* We don't need to prepend `::' for a qualified name;
- demangle_qualified will do that for us. */
- if (**mangled != 'Q')
- string_prepend (&decl, SCOPE_STRING (work));
-
- if (ISDIGIT ((unsigned char)**mangled))
- {
- n = consume_count (mangled);
- if (n == -1
- || (int) strlen (*mangled) < n)
- {
- success = 0;
- break;
- }
- string_prependn (&decl, *mangled, n);
- *mangled += n;
- }
- else if (**mangled == 'X' || **mangled == 'Y')
- {
- string temp;
- do_type (work, mangled, &temp);
- string_prepends (&decl, &temp);
- string_delete (&temp);
- }
- else if (**mangled == 't')
- {
- string temp;
- string_init (&temp);
- success = demangle_template (work, mangled, &temp,
- NULL, 1, 1);
- if (success)
- {
- string_prependn (&decl, temp.b, temp.p - temp.b);
- string_delete (&temp);
- }
- else
- {
- string_delete (&temp);
- break;
- }
- }
- else if (**mangled == 'Q')
- {
- success = demangle_qualified (work, mangled, &decl,
- /*isfuncnam=*/0,
- /*append=*/0);
- if (!success)
- break;
- }
- else
- {
- success = 0;
- break;
- }
-
- string_prepend (&decl, "(");
- if (member)
- {
- switch (**mangled)
- {
- case 'C':
- case 'V':
- case 'u':
- type_quals |= code_for_qualifier (**mangled);
- (*mangled)++;
- break;
-
- default:
- break;
- }
-
- if (*(*mangled) != 'F')
- {
- success = 0;
- break;
- }
- (*mangled)++;
- }
- if ((member && !demangle_nested_args (work, mangled, &decl))
- || **mangled != '_')
- {
- success = 0;
- break;
- }
- (*mangled)++;
- if (! PRINT_ANSI_QUALIFIERS)
- {
- break;
- }
- if (type_quals != TYPE_UNQUALIFIED)
- {
- APPEND_BLANK (&decl);
- string_append (&decl, qualifier_string (type_quals));
- }
- break;
- }
- case 'G':
- (*mangled)++;
- break;
-
- case 'C':
- case 'V':
- case 'u':
- if (PRINT_ANSI_QUALIFIERS)
- {
- if (!STRING_EMPTY (&decl))
- string_prepend (&decl, " ");
-
- string_prepend (&decl, demangle_qualifier (**mangled));
- }
- (*mangled)++;
- break;
- /*
- }
- */
-
- /* fall through */
- default:
- done = 1;
- break;
- }
- }
-
- if (success) switch (**mangled)
- {
- /* A qualified name, such as "Outer::Inner". */
- case 'Q':
- case 'K':
- {
- success = demangle_qualified (work, mangled, result, 0, 1);
- break;
- }
-
- /* A back reference to a previously seen squangled type */
- case 'B':
- (*mangled)++;
- if (!get_count (mangled, &n) || n < 0 || n >= work -> numb)
- success = 0;
- else
- string_append (result, work->btypevec[n]);
- break;
-
- case 'X':
- case 'Y':
- /* A template parm. We substitute the corresponding argument. */
- {
- int idx;
-
- (*mangled)++;
- idx = consume_count_with_underscores (mangled);
-
- if (idx == -1
- || (work->tmpl_argvec && idx >= work->ntmpl_args)
- || consume_count_with_underscores (mangled) == -1)
- {
- success = 0;
- break;
- }
-
- if (work->tmpl_argvec)
- string_append (result, work->tmpl_argvec[idx]);
- else
- string_append_template_idx (result, idx);
-
- success = 1;
- }
- break;
-
- default:
- success = demangle_fund_type (work, mangled, result);
- if (tk == tk_none)
- tk = (type_kind_t) success;
- break;
- }
-
- if (success)
- {
- if (!STRING_EMPTY (&decl))
- {
- string_append (result, " ");
- string_appends (result, &decl);
- }
- }
- else
- string_delete (result);
- string_delete (&decl);
-
- if (is_proctypevec)
- pop_processed_type (work);
-
- if (success)
- /* Assume an integral type, if we're not sure. */
- return (int) ((tk == tk_none) ? tk_integral : tk);
- else
- return 0;
-}
-
-/* Given a pointer to a type string that represents a fundamental type
- argument (int, long, unsigned int, etc) in TYPE, a pointer to the
- string in which the demangled output is being built in RESULT, and
- the WORK structure, decode the types and add them to the result.
-
- For example:
-
- "Ci" => "const int"
- "Sl" => "signed long"
- "CUs" => "const unsigned short"
-
- The value returned is really a type_kind_t. */
-
-static int
-demangle_fund_type (struct work_stuff *work,
- const char **mangled, string *result)
-{
- int done = 0;
- int success = 1;
- char buf[INTBUF_SIZE + 5 /* 'int%u_t' */];
- unsigned int dec = 0;
- type_kind_t tk = tk_integral;
-
- /* First pick off any type qualifiers. There can be more than one. */
-
- while (!done)
- {
- switch (**mangled)
- {
- case 'C':
- case 'V':
- case 'u':
- if (PRINT_ANSI_QUALIFIERS)
- {
- if (!STRING_EMPTY (result))
- string_prepend (result, " ");
- string_prepend (result, demangle_qualifier (**mangled));
- }
- (*mangled)++;
- break;
- case 'U':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "unsigned");
- break;
- case 'S': /* signed char only */
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "signed");
- break;
- case 'J':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "__complex");
- break;
- default:
- done = 1;
- break;
- }
- }
-
- /* Now pick off the fundamental type. There can be only one. */
-
- switch (**mangled)
- {
- case '\0':
- case '_':
- break;
- case 'v':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "void");
- break;
- case 'x':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "long long");
- break;
- case 'l':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "long");
- break;
- case 'i':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "int");
- break;
- case 's':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "short");
- break;
- case 'b':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "bool");
- tk = tk_bool;
- break;
- case 'c':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "char");
- tk = tk_char;
- break;
- case 'w':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "wchar_t");
- tk = tk_char;
- break;
- case 'r':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "long double");
- tk = tk_real;
- break;
- case 'd':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "double");
- tk = tk_real;
- break;
- case 'f':
- (*mangled)++;
- APPEND_BLANK (result);
- string_append (result, "float");
- tk = tk_real;
- break;
- case 'G':
- (*mangled)++;
- if (!ISDIGIT ((unsigned char)**mangled))
- {
- success = 0;
- break;
- }
- /* fall through */
- case 'I':
- (*mangled)++;
- if (**mangled == '_')
- {
- int i;
- (*mangled)++;
- for (i = 0;
- i < (long) sizeof (buf) - 1 && **mangled && **mangled != '_';
- (*mangled)++, i++)
- buf[i] = **mangled;
- if (**mangled != '_')
- {
- success = 0;
- break;
- }
- buf[i] = '\0';
- (*mangled)++;
- }
- else
- {
- strncpy (buf, *mangled, 2);
- buf[2] = '\0';
- *mangled += min (strlen (*mangled), 2);
- }
- sscanf (buf, "%x", &dec);
- sprintf (buf, "int%u_t", dec);
- APPEND_BLANK (result);
- string_append (result, buf);
- break;
-
- /* fall through */
- /* An explicit type, such as "6mytype" or "7integer" */
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- int bindex = register_Btype (work);
- string btype;
- string_init (&btype);
- if (demangle_class_name (work, mangled, &btype)) {
- remember_Btype (work, btype.b, LEN_STRING (&btype), bindex);
- APPEND_BLANK (result);
- string_appends (result, &btype);
- }
- else
- success = 0;
- string_delete (&btype);
- break;
- }
- case 't':
- {
- string btype;
- string_init (&btype);
- success = demangle_template (work, mangled, &btype, 0, 1, 1);
- string_appends (result, &btype);
- string_delete (&btype);
- break;
- }
- default:
- success = 0;
- break;
- }
-
- return success ? ((int) tk) : 0;
-}
-
-
-/* Handle a template's value parameter for HP aCC (extension from ARM)
- **mangled points to 'S' or 'U' */
-
-static int
-do_hpacc_template_const_value (struct work_stuff *work ATTRIBUTE_UNUSED,
- const char **mangled, string *result)
-{
- int unsigned_const;
-
- if (**mangled != 'U' && **mangled != 'S')
- return 0;
-
- unsigned_const = (**mangled == 'U');
-
- (*mangled)++;
-
- switch (**mangled)
- {
- case 'N':
- string_append (result, "-");
- /* fall through */
- case 'P':
- (*mangled)++;
- break;
- case 'M':
- /* special case for -2^31 */
- string_append (result, "-2147483648");
- (*mangled)++;
- return 1;
- default:
- return 0;
- }
-
- /* We have to be looking at an integer now */
- if (!(ISDIGIT ((unsigned char)**mangled)))
- return 0;
-
- /* We only deal with integral values for template
- parameters -- so it's OK to look only for digits */
- while (ISDIGIT ((unsigned char)**mangled))
- {
- char_str[0] = **mangled;
- string_append (result, char_str);
- (*mangled)++;
- }
-
- if (unsigned_const)
- string_append (result, "U");
-
- /* FIXME? Some day we may have 64-bit (or larger :-) ) constants
- with L or LL suffixes. pai/1997-09-03 */
-
- return 1; /* success */
-}
-
-/* Handle a template's literal parameter for HP aCC (extension from ARM)
- **mangled is pointing to the 'A' */
-
-static int
-do_hpacc_template_literal (struct work_stuff *work, const char **mangled,
- string *result)
-{
- int literal_len = 0;
- char * recurse;
- char * recurse_dem;
-
- if (**mangled != 'A')
- return 0;
-
- (*mangled)++;
-
- literal_len = consume_count (mangled);
-
- if (literal_len <= 0
- || literal_len > (long) strlen (*mangled))
- return 0;
-
- /* Literal parameters are names of arrays, functions, etc. and the
- canonical representation uses the address operator */
- string_append (result, "&");
-
- /* Now recursively demangle the literal name */
- recurse = XNEWVEC (char, literal_len + 1);
- memcpy (recurse, *mangled, literal_len);
- recurse[literal_len] = '\000';
-
- recurse_dem = cplus_demangle (recurse, work->options);
-
- if (recurse_dem)
- {
- string_append (result, recurse_dem);
- free (recurse_dem);
- }
- else
- {
- string_appendn (result, *mangled, literal_len);
- }
- (*mangled) += literal_len;
- free (recurse);
-
- return 1;
-}
-
-static int
-snarf_numeric_literal (const char **args, string *arg)
-{
- if (**args == '-')
- {
- char_str[0] = '-';
- string_append (arg, char_str);
- (*args)++;
- }
- else if (**args == '+')
- (*args)++;
-
- if (!ISDIGIT ((unsigned char)**args))
- return 0;
-
- while (ISDIGIT ((unsigned char)**args))
- {
- char_str[0] = **args;
- string_append (arg, char_str);
- (*args)++;
- }
-
- return 1;
-}
-
-/* Demangle the next argument, given by MANGLED into RESULT, which
- *should be an uninitialized* string. It will be initialized here,
- and free'd should anything go wrong. */
-
-static int
-do_arg (struct work_stuff *work, const char **mangled, string *result)
-{
- /* Remember where we started so that we can record the type, for
- non-squangling type remembering. */
- const char *start = *mangled;
-
- string_init (result);
-
- if (work->nrepeats > 0)
- {
- --work->nrepeats;
-
- if (work->previous_argument == 0)
- return 0;
-
- /* We want to reissue the previous type in this argument list. */
- string_appends (result, work->previous_argument);
- return 1;
- }
-
- if (**mangled == 'n')
- {
- /* A squangling-style repeat. */
- (*mangled)++;
- work->nrepeats = consume_count(mangled);
-
- if (work->nrepeats <= 0)
- /* This was not a repeat count after all. */
- return 0;
-
- if (work->nrepeats > 9)
- {
- if (**mangled != '_')
- /* The repeat count should be followed by an '_' in this
- case. */
- return 0;
- else
- (*mangled)++;
- }
-
- /* Now, the repeat is all set up. */
- return do_arg (work, mangled, result);
- }
-
- /* Save the result in WORK->previous_argument so that we can find it
- if it's repeated. Note that saving START is not good enough: we
- do not want to add additional types to the back-referenceable
- type vector when processing a repeated type. */
- if (work->previous_argument)
- string_delete (work->previous_argument);
- else
- work->previous_argument = XNEW (string);
-
- if (!do_type (work, mangled, work->previous_argument))
- return 0;
-
- string_appends (result, work->previous_argument);
-
- remember_type (work, start, *mangled - start);
- return 1;
-}
-
-static void
-push_processed_type (struct work_stuff *work, int typevec_index)
-{
- if (work->nproctypes >= work->proctypevec_size)
- {
- if (!work->proctypevec_size)
- {
- work->proctypevec_size = 4;
- work->proctypevec = XNEWVEC (int, work->proctypevec_size);
- }
- else
- {
- if (work->proctypevec_size < 16)
- /* Double when small. */
- work->proctypevec_size *= 2;
- else
- {
- /* Grow slower when large. */
- if (work->proctypevec_size > (INT_MAX / 3) * 2)
- xmalloc_failed (INT_MAX);
- work->proctypevec_size = (work->proctypevec_size * 3 / 2);
- }
- work->proctypevec
- = XRESIZEVEC (int, work->proctypevec, work->proctypevec_size);
- }
- }
- work->proctypevec [work->nproctypes++] = typevec_index;
-}
-
-static void
-pop_processed_type (struct work_stuff *work)
-{
- work->nproctypes--;
-}
-
-static void
-remember_type (struct work_stuff *work, const char *start, int len)
-{
- char *tem;
-
- if (work->forgetting_types)
- return;
-
- if (work -> ntypes >= work -> typevec_size)
- {
- if (work -> typevec_size == 0)
- {
- work -> typevec_size = 3;
- work -> typevec = XNEWVEC (char *, work->typevec_size);
- }
- else
- {
- if (work -> typevec_size > INT_MAX / 2)
- xmalloc_failed (INT_MAX);
- work -> typevec_size *= 2;
- work -> typevec
- = XRESIZEVEC (char *, work->typevec, work->typevec_size);
- }
- }
- tem = XNEWVEC (char, len + 1);
- memcpy (tem, start, len);
- tem[len] = '\0';
- work -> typevec[work -> ntypes++] = tem;
-}
-
-
-/* Remember a K type class qualifier. */
-static void
-remember_Ktype (struct work_stuff *work, const char *start, int len)
-{
- char *tem;
-
- if (work -> numk >= work -> ksize)
- {
- if (work -> ksize == 0)
- {
- work -> ksize = 5;
- work -> ktypevec = XNEWVEC (char *, work->ksize);
- }
- else
- {
- if (work -> ksize > INT_MAX / 2)
- xmalloc_failed (INT_MAX);
- work -> ksize *= 2;
- work -> ktypevec
- = XRESIZEVEC (char *, work->ktypevec, work->ksize);
- }
- }
- tem = XNEWVEC (char, len + 1);
- memcpy (tem, start, len);
- tem[len] = '\0';
- work -> ktypevec[work -> numk++] = tem;
-}
-
-/* Register a B code, and get an index for it. B codes are registered
- as they are seen, rather than as they are completed, so map<temp<char> >
- registers map<temp<char> > as B0, and temp<char> as B1 */
-
-static int
-register_Btype (struct work_stuff *work)
-{
- int ret;
-
- if (work -> numb >= work -> bsize)
- {
- if (work -> bsize == 0)
- {
- work -> bsize = 5;
- work -> btypevec = XNEWVEC (char *, work->bsize);
- }
- else
- {
- if (work -> bsize > INT_MAX / 2)
- xmalloc_failed (INT_MAX);
- work -> bsize *= 2;
- work -> btypevec
- = XRESIZEVEC (char *, work->btypevec, work->bsize);
- }
- }
- ret = work -> numb++;
- work -> btypevec[ret] = NULL;
- return(ret);
-}
-
-/* Store a value into a previously registered B code type. */
-
-static void
-remember_Btype (struct work_stuff *work, const char *start,
- int len, int index)
-{
- char *tem;
-
- tem = XNEWVEC (char, len + 1);
- memcpy (tem, start, len);
- tem[len] = '\0';
- work -> btypevec[index] = tem;
-}
-
-/* Lose all the info related to B and K type codes. */
-static void
-forget_B_and_K_types (struct work_stuff *work)
-{
- int i;
-
- while (work -> numk > 0)
- {
- i = --(work -> numk);
- if (work -> ktypevec[i] != NULL)
- {
- free (work -> ktypevec[i]);
- work -> ktypevec[i] = NULL;
- }
- }
-
- while (work -> numb > 0)
- {
- i = --(work -> numb);
- if (work -> btypevec[i] != NULL)
- {
- free (work -> btypevec[i]);
- work -> btypevec[i] = NULL;
- }
- }
-}
-/* Forget the remembered types, but not the type vector itself. */
-
-static void
-forget_types (struct work_stuff *work)
-{
- int i;
-
- while (work -> ntypes > 0)
- {
- i = --(work -> ntypes);
- if (work -> typevec[i] != NULL)
- {
- free (work -> typevec[i]);
- work -> typevec[i] = NULL;
- }
- }
-}
-
-/* Process the argument list part of the signature, after any class spec
- has been consumed, as well as the first 'F' character (if any). For
- example:
-
- "__als__3fooRT0" => process "RT0"
- "complexfunc5__FPFPc_PFl_i" => process "PFPc_PFl_i"
-
- DECLP must be already initialised, usually non-empty. It won't be freed
- on failure.
-
- Note that g++ differs significantly from ARM and lucid style mangling
- with regards to references to previously seen types. For example, given
- the source fragment:
-
- class foo {
- public:
- foo::foo (int, foo &ia, int, foo &ib, int, foo &ic);
- };
-
- foo::foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; }
- void foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; }
-
- g++ produces the names:
-
- __3fooiRT0iT2iT2
- foo__FiR3fooiT1iT1
-
- while lcc (and presumably other ARM style compilers as well) produces:
-
- foo__FiR3fooT1T2T1T2
- __ct__3fooFiR3fooT1T2T1T2
-
- Note that g++ bases its type numbers starting at zero and counts all
- previously seen types, while lucid/ARM bases its type numbers starting
- at one and only considers types after it has seen the 'F' character
- indicating the start of the function args. For lucid/ARM style, we
- account for this difference by discarding any previously seen types when
- we see the 'F' character, and subtracting one from the type number
- reference.
-
- */
-
-static int
-demangle_args (struct work_stuff *work, const char **mangled,
- string *declp)
-{
- string arg;
- int need_comma = 0;
- int r;
- int t;
- const char *tem;
- char temptype;
-
- if (PRINT_ARG_TYPES)
- {
- string_append (declp, "(");
- if (**mangled == '\0')
- {
- string_append (declp, "void");
- }
- }
-
- while ((**mangled != '_' && **mangled != '\0' && **mangled != 'e')
- || work->nrepeats > 0)
- {
- if ((**mangled == 'N') || (**mangled == 'T'))
- {
- temptype = *(*mangled)++;
-
- if (temptype == 'N')
- {
- if (!get_count (mangled, &r))
- {
- return (0);
- }
- }
- else
- {
- r = 1;
- }
- if ((HP_DEMANGLING || ARM_DEMANGLING || EDG_DEMANGLING) && work -> ntypes >= 10)
- {
- /* If we have 10 or more types we might have more than a 1 digit
- index so we'll have to consume the whole count here. This
- will lose if the next thing is a type name preceded by a
- count but it's impossible to demangle that case properly
- anyway. Eg if we already have 12 types is T12Pc "(..., type1,
- Pc, ...)" or "(..., type12, char *, ...)" */
- if ((t = consume_count(mangled)) <= 0)
- {
- return (0);
- }
- }
- else
- {
- if (!get_count (mangled, &t))
- {
- return (0);
- }
- }
- if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING)
- {
- t--;
- }
- /* Validate the type index. Protect against illegal indices from
- malformed type strings. */
- if ((t < 0) || (t >= work -> ntypes))
- {
- return (0);
- }
- while (work->nrepeats > 0 || --r >= 0)
- {
- tem = work -> typevec[t];
- if (need_comma && PRINT_ARG_TYPES)
- {
- string_append (declp, ", ");
- }
- push_processed_type (work, t);
- if (!do_arg (work, &tem, &arg))
- {
- pop_processed_type (work);
- return (0);
- }
- pop_processed_type (work);
- if (PRINT_ARG_TYPES)
- {
- string_appends (declp, &arg);
- }
- string_delete (&arg);
- need_comma = 1;
- }
- }
- else
- {
- if (need_comma && PRINT_ARG_TYPES)
- string_append (declp, ", ");
- if (!do_arg (work, mangled, &arg))
- return (0);
- if (PRINT_ARG_TYPES)
- string_appends (declp, &arg);
- string_delete (&arg);
- need_comma = 1;
- }
- }
-
- if (**mangled == 'e')
- {
- (*mangled)++;
- if (PRINT_ARG_TYPES)
- {
- if (need_comma)
- {
- string_append (declp, ",");
- }
- string_append (declp, "...");
- }
- }
-
- if (PRINT_ARG_TYPES)
- {
- string_append (declp, ")");
- }
- return (1);
-}
-
-/* Like demangle_args, but for demangling the argument lists of function
- and method pointers or references, not top-level declarations. */
-
-static int
-demangle_nested_args (struct work_stuff *work, const char **mangled,
- string *declp)
-{
- string* saved_previous_argument;
- int result;
- int saved_nrepeats;
-
- /* The G++ name-mangling algorithm does not remember types on nested
- argument lists, unless -fsquangling is used, and in that case the
- type vector updated by remember_type is not used. So, we turn
- off remembering of types here. */
- ++work->forgetting_types;
-
- /* For the repeat codes used with -fsquangling, we must keep track of
- the last argument. */
- saved_previous_argument = work->previous_argument;
- saved_nrepeats = work->nrepeats;
- work->previous_argument = 0;
- work->nrepeats = 0;
-
- /* Actually demangle the arguments. */
- result = demangle_args (work, mangled, declp);
-
- /* Restore the previous_argument field. */
- if (work->previous_argument)
- {
- string_delete (work->previous_argument);
- free ((char *) work->previous_argument);
- }
- work->previous_argument = saved_previous_argument;
- --work->forgetting_types;
- work->nrepeats = saved_nrepeats;
-
- return result;
-}
-
-/* Returns 1 if a valid function name was found or 0 otherwise. */
-
-static int
-demangle_function_name (struct work_stuff *work, const char **mangled,
- string *declp, const char *scan)
-{
- size_t i;
- string type;
- const char *tem;
-
- string_appendn (declp, (*mangled), scan - (*mangled));
- string_need (declp, 1);
- *(declp -> p) = '\0';
-
- /* Consume the function name, including the "__" separating the name
- from the signature. We are guaranteed that SCAN points to the
- separator. */
-
- (*mangled) = scan + 2;
- /* We may be looking at an instantiation of a template function:
- foo__Xt1t2_Ft3t4, where t1, t2, ... are template arguments and a
- following _F marks the start of the function arguments. Handle
- the template arguments first. */
-
- if (HP_DEMANGLING && (**mangled == 'X'))
- {
- demangle_arm_hp_template (work, mangled, 0, declp);
- /* This leaves MANGLED pointing to the 'F' marking func args */
- }
-
- if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING)
- {
-
- /* See if we have an ARM style constructor or destructor operator.
- If so, then just record it, clear the decl, and return.
- We can't build the actual constructor/destructor decl until later,
- when we recover the class name from the signature. */
-
- if (strcmp (declp -> b, "__ct") == 0)
- {
- work -> constructor += 1;
- string_clear (declp);
- return 1;
- }
- else if (strcmp (declp -> b, "__dt") == 0)
- {
- work -> destructor += 1;
- string_clear (declp);
- return 1;
- }
- }
-
- if (declp->p - declp->b >= 3
- && declp->b[0] == 'o'
- && declp->b[1] == 'p'
- && strchr (cplus_markers, declp->b[2]) != NULL)
- {
- /* see if it's an assignment expression */
- if (declp->p - declp->b >= 10 /* op$assign_ */
- && memcmp (declp->b + 3, "assign_", 7) == 0)
- {
- for (i = 0; i < ARRAY_SIZE (optable); i++)
- {
- int len = declp->p - declp->b - 10;
- if ((int) strlen (optable[i].in) == len
- && memcmp (optable[i].in, declp->b + 10, len) == 0)
- {
- string_clear (declp);
- string_append (declp, "operator");
- string_append (declp, optable[i].out);
- string_append (declp, "=");
- break;
- }
- }
- }
- else
- {
- for (i = 0; i < ARRAY_SIZE (optable); i++)
- {
- int len = declp->p - declp->b - 3;
- if ((int) strlen (optable[i].in) == len
- && memcmp (optable[i].in, declp->b + 3, len) == 0)
- {
- string_clear (declp);
- string_append (declp, "operator");
- string_append (declp, optable[i].out);
- break;
- }
- }
- }
- }
- else if (declp->p - declp->b >= 5 && memcmp (declp->b, "type", 4) == 0
- && strchr (cplus_markers, declp->b[4]) != NULL)
- {
- /* type conversion operator */
- tem = declp->b + 5;
- if (do_type (work, &tem, &type))
- {
- string_clear (declp);
- string_append (declp, "operator ");
- string_appends (declp, &type);
- string_delete (&type);
- }
- }
- else if (declp->b[0] == '_' && declp->b[1] == '_'
- && declp->b[2] == 'o' && declp->b[3] == 'p')
- {
- /* ANSI. */
- /* type conversion operator. */
- tem = declp->b + 4;
- if (do_type (work, &tem, &type))
- {
- string_clear (declp);
- string_append (declp, "operator ");
- string_appends (declp, &type);
- string_delete (&type);
- }
- }
- else if (declp->b[0] == '_' && declp->b[1] == '_'
- && ISLOWER((unsigned char)declp->b[2])
- && ISLOWER((unsigned char)declp->b[3]))
- {
- if (declp->b[4] == '\0')
- {
- /* Operator. */
- for (i = 0; i < ARRAY_SIZE (optable); i++)
- {
- if (strlen (optable[i].in) == 2
- && memcmp (optable[i].in, declp->b + 2, 2) == 0)
- {
- string_clear (declp);
- string_append (declp, "operator");
- string_append (declp, optable[i].out);
- break;
- }
- }
- }
- else
- {
- if (declp->b[2] == 'a' && declp->b[5] == '\0')
- {
- /* Assignment. */
- for (i = 0; i < ARRAY_SIZE (optable); i++)
- {
- if (strlen (optable[i].in) == 3
- && memcmp (optable[i].in, declp->b + 2, 3) == 0)
- {
- string_clear (declp);
- string_append (declp, "operator");
- string_append (declp, optable[i].out);
- break;
- }
- }
- }
- }
- }
-
- /* If a function name was obtained but it's not valid, we were not
- successful. */
- if (LEN_STRING (declp) == 1 && declp->b[0] == '.')
- return 0;
- else
- return 1;
-}
-
-/* a mini string-handling package */
-
-static void
-string_need (string *s, int n)
-{
- int tem;
-
- if (s->b == NULL)
- {
- if (n < 32)
- {
- n = 32;
- }
- s->p = s->b = XNEWVEC (char, n);
- s->e = s->b + n;
- }
- else if (s->e - s->p < n)
- {
- tem = s->p - s->b;
- if (n > INT_MAX / 2 - tem)
- xmalloc_failed (INT_MAX);
- n += tem;
- n *= 2;
- s->b = XRESIZEVEC (char, s->b, n);
- s->p = s->b + tem;
- s->e = s->b + n;
- }
-}
-
-static void
-string_delete (string *s)
-{
- if (s->b != NULL)
- {
- free (s->b);
- s->b = s->e = s->p = NULL;
- }
-}
-
-static void
-string_init (string *s)
-{
- s->b = s->p = s->e = NULL;
-}
-
-static void
-string_clear (string *s)
-{
- s->p = s->b;
-}
-
-#if 0
-
-static int
-string_empty (string *s)
-{
- return (s->b == s->p);
-}
-
-#endif
-
-static void
-string_append (string *p, const char *s)
-{
- int n;
- if (s == NULL || *s == '\0')
- return;
- n = strlen (s);
- string_need (p, n);
- memcpy (p->p, s, n);
- p->p += n;
-}
-
-static void
-string_appends (string *p, string *s)
-{
- int n;
-
- if (s->b != s->p)
- {
- n = s->p - s->b;
- string_need (p, n);
- memcpy (p->p, s->b, n);
- p->p += n;
- }
-}
-
-static void
-string_appendn (string *p, const char *s, int n)
-{
- if (n != 0)
- {
- string_need (p, n);
- memcpy (p->p, s, n);
- p->p += n;
- }
-}
-
-static void
-string_prepend (string *p, const char *s)
-{
- if (s != NULL && *s != '\0')
- {
- string_prependn (p, s, strlen (s));
- }
-}
-
-static void
-string_prepends (string *p, string *s)
-{
- if (s->b != s->p)
- {
- string_prependn (p, s->b, s->p - s->b);
- }
-}
-
-static void
-string_prependn (string *p, const char *s, int n)
-{
- char *q;
-
- if (n != 0)
- {
- string_need (p, n);
- for (q = p->p - 1; q >= p->b; q--)
- {
- q[n] = q[0];
- }
- memcpy (p->b, s, n);
- p->p += n;
- }
-}
-
-static void
-string_append_template_idx (string *s, int idx)
-{
- char buf[INTBUF_SIZE + 1 /* 'T' */];
- sprintf(buf, "T%d", idx);
- string_append (s, buf);
-}
diff --git a/rtemstoolkit/libiberty/d-demangle.c b/rtemstoolkit/libiberty/d-demangle.c
index 08690de..c41ad02 100644
--- a/rtemstoolkit/libiberty/d-demangle.c
+++ b/rtemstoolkit/libiberty/d-demangle.c
@@ -1,5 +1,5 @@
/* Demangler for the D programming language
- Copyright (C) 2014-2017 Free Software Foundation, Inc.
+ Copyright (C) 2014-2023 Free Software Foundation, Inc.
Written by Iain Buclaw (ibuclaw@gdcproject.org)
This file is part of the libiberty library.
@@ -31,6 +31,9 @@ If not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
#include "safe-ctype.h"
@@ -45,6 +48,13 @@ If not, see <http://www.gnu.org/licenses/>. */
#include <demangle.h>
#include "libiberty.h"
+#ifndef ULONG_MAX
+#define ULONG_MAX (~0UL)
+#endif
+#ifndef UINT_MAX
+#define UINT_MAX (~0U)
+#endif
+
/* A mini string-handling package */
typedef struct string /* Beware: these aren't required to be */
@@ -55,9 +65,9 @@ typedef struct string /* Beware: these aren't required to be */
} string;
static void
-string_need (string *s, int n)
+string_need (string *s, size_t n)
{
- int tem;
+ size_t tem;
if (s->b == NULL)
{
@@ -68,7 +78,7 @@ string_need (string *s, int n)
s->p = s->b = XNEWVEC (char, n);
s->e = s->b + n;
}
- else if (s->e - s->p < n)
+ else if ((size_t) (s->e - s->p) < n)
{
tem = s->p - s->b;
n += tem;
@@ -117,14 +127,14 @@ string_setlength (string *s, int n)
static void
string_append (string *p, const char *s)
{
- int n = strlen (s);
+ size_t n = strlen (s);
string_need (p, n);
memcpy (p->p, s, n);
p->p += n;
}
static void
-string_appendn (string *p, const char *s, int n)
+string_appendn (string *p, const char *s, size_t n)
{
if (n != 0)
{
@@ -135,7 +145,7 @@ string_appendn (string *p, const char *s, int n)
}
static void
-string_prependn (string *p, const char *s, int n)
+string_prependn (string *p, const char *s, size_t n)
{
char *q;
@@ -160,66 +170,73 @@ string_prepend (string *p, const char *s)
}
}
-/* What kinds of symbol we could be parsing. */
-enum dlang_symbol_kinds
+/* Demangle information structure we pass around. */
+struct dlang_info
{
- /* Top-level symbol, needs it's type checked. */
- dlang_top_level,
- /* Function symbol, needs it's type checked. */
- dlang_function,
- /* Strongly typed name, such as for classes, structs and enums. */
- dlang_type_name,
- /* Template identifier. */
- dlang_template_ident,
- /* Template symbol parameter. */
- dlang_template_param
+ /* The string we are demangling. */
+ const char *s;
+ /* The index of the last back reference. */
+ int last_backref;
};
+/* Pass as the LEN to dlang_parse_template if symbol length is not known. */
+#define TEMPLATE_LENGTH_UNKNOWN (-1UL)
+
/* Prototypes for forward referenced functions */
-static const char *dlang_function_args (string *, const char *);
+static const char *dlang_function_type (string *, const char *,
+ struct dlang_info *);
+
+static const char *dlang_function_args (string *, const char *,
+ struct dlang_info *);
-static const char *dlang_type (string *, const char *);
+static const char *dlang_type (string *, const char *, struct dlang_info *);
-static const char *dlang_value (string *, const char *, const char *, char);
+static const char *dlang_value (string *, const char *, const char *, char,
+ struct dlang_info *);
static const char *dlang_parse_qualified (string *, const char *,
- enum dlang_symbol_kinds);
+ struct dlang_info *, int);
static const char *dlang_parse_mangle (string *, const char *,
- enum dlang_symbol_kinds);
+ struct dlang_info *);
+
+static const char *dlang_parse_tuple (string *, const char *,
+ struct dlang_info *);
-static const char *dlang_parse_tuple (string *, const char *);
+static const char *dlang_parse_template (string *, const char *,
+ struct dlang_info *, unsigned long);
-static const char *dlang_parse_template (string *, const char *, long);
+static const char *dlang_lname (string *, const char *, unsigned long);
/* Extract the number from MANGLED, and assign the result to RET.
- Return the remaining string on success or NULL on failure. */
+ Return the remaining string on success or NULL on failure.
+ A result larger than UINT_MAX is considered a failure. */
static const char *
-dlang_number (const char *mangled, long *ret)
+dlang_number (const char *mangled, unsigned long *ret)
{
/* Return NULL if trying to extract something that isn't a digit. */
if (mangled == NULL || !ISDIGIT (*mangled))
return NULL;
- (*ret) = 0;
+ unsigned long val = 0;
while (ISDIGIT (*mangled))
{
- (*ret) *= 10;
+ unsigned long digit = mangled[0] - '0';
- /* If an overflow occured when multiplying by ten, the result
- will not be a multiple of ten. */
- if ((*ret % 10) != 0)
+ /* Check for overflow. */
+ if (val > (UINT_MAX - digit) / 10)
return NULL;
- (*ret) += mangled[0] - '0';
+ val = val * 10 + digit;
mangled++;
}
- if (*mangled == '\0' || *ret < 0)
+ if (*mangled == '\0')
return NULL;
+ *ret = val;
return mangled;
}
@@ -236,15 +253,15 @@ dlang_hexdigit (const char *mangled, char *ret)
c = mangled[0];
if (!ISDIGIT (c))
- (*ret) = (c - (ISUPPER (c) ? 'A' : 'a') + 10);
+ *ret = c - (ISUPPER (c) ? 'A' : 'a') + 10;
else
- (*ret) = (c - '0');
+ *ret = c - '0';
c = mangled[1];
if (!ISDIGIT (c))
- (*ret) = (*ret << 4) | (c - (ISUPPER (c) ? 'A' : 'a') + 10);
+ *ret = (*ret << 4) | (c - (ISUPPER (c) ? 'A' : 'a') + 10);
else
- (*ret) = (*ret << 4) | (c - '0');
+ *ret = (*ret << 4) | (c - '0');
mangled += 2;
@@ -267,6 +284,178 @@ dlang_call_convention_p (const char *mangled)
}
}
+/* Extract the back reference position from MANGLED, and assign the result
+ to RET. Return the remaining string on success or NULL on failure.
+ A result <= 0 is a failure. */
+static const char *
+dlang_decode_backref (const char *mangled, long *ret)
+{
+ /* Return NULL if trying to extract something that isn't a digit. */
+ if (mangled == NULL || !ISALPHA (*mangled))
+ return NULL;
+
+ /* Any identifier or non-basic type that has been emitted to the mangled
+ symbol before will not be emitted again, but is referenced by a special
+ sequence encoding the relative position of the original occurrence in the
+ mangled symbol name.
+
+ Numbers in back references are encoded with base 26 by upper case letters
+ A-Z for higher digits but lower case letters a-z for the last digit.
+
+ NumberBackRef:
+ [a-z]
+ [A-Z] NumberBackRef
+ ^
+ */
+ unsigned long val = 0;
+
+ while (ISALPHA (*mangled))
+ {
+ /* Check for overflow. */
+ if (val > (ULONG_MAX - 25) / 26)
+ break;
+
+ val *= 26;
+
+ if (mangled[0] >= 'a' && mangled[0] <= 'z')
+ {
+ val += mangled[0] - 'a';
+ if ((long) val <= 0)
+ break;
+ *ret = val;
+ return mangled + 1;
+ }
+
+ val += mangled[0] - 'A';
+ mangled++;
+ }
+
+ return NULL;
+}
+
+/* Extract the symbol pointed at by the back reference and assign the result
+ to RET. Return the remaining string on success or NULL on failure. */
+static const char *
+dlang_backref (const char *mangled, const char **ret, struct dlang_info *info)
+{
+ *ret = NULL;
+
+ if (mangled == NULL || *mangled != 'Q')
+ return NULL;
+
+ /* Position of 'Q'. */
+ const char *qpos = mangled;
+ long refpos;
+ mangled++;
+
+ mangled = dlang_decode_backref (mangled, &refpos);
+ if (mangled == NULL)
+ return NULL;
+
+ if (refpos > qpos - info->s)
+ return NULL;
+
+ /* Set the position of the back reference. */
+ *ret = qpos - refpos;
+
+ return mangled;
+}
+
+/* Demangle a back referenced symbol from MANGLED and append it to DECL.
+ Return the remaining string on success or NULL on failure. */
+static const char *
+dlang_symbol_backref (string *decl, const char *mangled,
+ struct dlang_info *info)
+{
+ /* An identifier back reference always points to a digit 0 to 9.
+
+ IdentifierBackRef:
+ Q NumberBackRef
+ ^
+ */
+ const char *backref;
+ unsigned long len;
+
+ /* Get position of the back reference. */
+ mangled = dlang_backref (mangled, &backref, info);
+
+ /* Must point to a simple identifier. */
+ backref = dlang_number (backref, &len);
+ if (backref == NULL || strlen(backref) < len)
+ return NULL;
+
+ backref = dlang_lname (decl, backref, len);
+ if (backref == NULL)
+ return NULL;
+
+ return mangled;
+}
+
+/* Demangle a back referenced type from MANGLED and append it to DECL.
+ IS_FUNCTION is 1 if the back referenced type is expected to be a function.
+ Return the remaining string on success or NULL on failure. */
+static const char *
+dlang_type_backref (string *decl, const char *mangled, struct dlang_info *info,
+ int is_function)
+{
+ /* A type back reference always points to a letter.
+
+ TypeBackRef:
+ Q NumberBackRef
+ ^
+ */
+ const char *backref;
+
+ /* If we appear to be moving backwards through the mangle string, then
+ bail as this may be a recursive back reference. */
+ if (mangled - info->s >= info->last_backref)
+ return NULL;
+
+ int save_refpos = info->last_backref;
+ info->last_backref = mangled - info->s;
+
+ /* Get position of the back reference. */
+ mangled = dlang_backref (mangled, &backref, info);
+
+ /* Must point to a type. */
+ if (is_function)
+ backref = dlang_function_type (decl, backref, info);
+ else
+ backref = dlang_type (decl, backref, info);
+
+ info->last_backref = save_refpos;
+
+ if (backref == NULL)
+ return NULL;
+
+ return mangled;
+}
+
+/* Extract the beginning of a symbol name from MANGLED and
+ return 1 on success or 0 on failure. */
+static int
+dlang_symbol_name_p (const char *mangled, struct dlang_info *info)
+{
+ long ret;
+ const char *qref = mangled;
+
+ if (ISDIGIT (*mangled))
+ return 1;
+
+ if (mangled[0] == '_' && mangled[1] == '_'
+ && (mangled[2] == 'T' || mangled[2] == 'U'))
+ return 1;
+
+ if (*mangled != 'Q')
+ return 0;
+
+ mangled = dlang_decode_backref (mangled + 1, &ret);
+ if (mangled == NULL || ret > qref - info->s)
+ return 0;
+
+ return ISDIGIT (qref[-ret]);
+}
+
/* Demangle the calling convention from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
@@ -385,9 +574,11 @@ dlang_attributes (string *decl, const char *mangled)
case 'g':
case 'h':
case 'k':
+ case 'n':
/* inout parameter is represented as 'Ng'.
vector parameter is represented as 'Nh'.
- return paramenter is represented as 'Nk'.
+ return parameter is represented as 'Nk'.
+ typeof(*null) parameter is represented as 'Nn'.
If we see this, then we know we're really in the
parameter list. Rewind and break. */
mangled--;
@@ -404,6 +595,10 @@ dlang_attributes (string *decl, const char *mangled)
mangled++;
string_append (decl, "scope ");
continue;
+ case 'm': /* @live */
+ mangled++;
+ string_append (decl, "@live ");
+ continue;
default: /* unknown attribute */
return NULL;
@@ -414,13 +609,39 @@ dlang_attributes (string *decl, const char *mangled)
return mangled;
}
+/* Demangle the function type from MANGLED without the return type.
+ The arguments are appended to ARGS, the calling convention is appended
+ to CALL and attributes are appended to ATTR. Any of these can be NULL
+ to throw the information away. Return the remaining string on success
+ or NULL on failure. */
+static const char *
+dlang_function_type_noreturn (string *args, string *call, string *attr,
+ const char *mangled, struct dlang_info *info)
+{
+ string dump;
+ string_init (&dump);
+
+ /* Skip over calling convention and attributes. */
+ mangled = dlang_call_convention (call ? call : &dump, mangled);
+ mangled = dlang_attributes (attr ? attr : &dump, mangled);
+
+ if (args)
+ string_append (args, "(");
+
+ mangled = dlang_function_args (args ? args : &dump, mangled, info);
+ if (args)
+ string_append (args, ")");
+
+ string_delete (&dump);
+ return mangled;
+}
+
/* Demangle the function type from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
-dlang_function_type (string *decl, const char *mangled)
+dlang_function_type (string *decl, const char *mangled, struct dlang_info *info)
{
string attr, args, type;
- size_t szattr, szargs, sztype;
if (mangled == NULL || *mangled == '\0')
return NULL;
@@ -435,27 +656,16 @@ dlang_function_type (string *decl, const char *mangled)
string_init (&args);
string_init (&type);
- /* Function call convention. */
- mangled = dlang_call_convention (decl, mangled);
-
- /* Function attributes. */
- mangled = dlang_attributes (&attr, mangled);
- szattr = string_length (&attr);
-
- /* Function arguments. */
- mangled = dlang_function_args (&args, mangled);
- szargs = string_length (&args);
+ mangled = dlang_function_type_noreturn (&args, decl, &attr, mangled, info);
/* Function return type. */
- mangled = dlang_type (&type, mangled);
- sztype = string_length (&type);
+ mangled = dlang_type (&type, mangled, info);
/* Append to decl in order. */
- string_appendn (decl, type.b, sztype);
- string_append (decl, "(");
- string_appendn (decl, args.b, szargs);
- string_append (decl, ") ");
- string_appendn (decl, attr.b, szattr);
+ string_appendn (decl, type.b, string_length (&type));
+ string_appendn (decl, args.b, string_length (&args));
+ string_append (decl, " ");
+ string_appendn (decl, attr.b, string_length (&attr));
string_delete (&attr);
string_delete (&args);
@@ -466,7 +676,7 @@ dlang_function_type (string *decl, const char *mangled)
/* Demangle the argument list from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
-dlang_function_args (string *decl, const char *mangled)
+dlang_function_args (string *decl, const char *mangled, struct dlang_info *info)
{
size_t n = 0;
@@ -506,6 +716,15 @@ dlang_function_args (string *decl, const char *mangled)
switch (*mangled)
{
+ case 'I': /* in(T) */
+ mangled++;
+ string_append (decl, "in ");
+ if (*mangled == 'K') /* in ref(T) */
+ {
+ mangled++;
+ string_append (decl, "ref ");
+ }
+ break;
case 'J': /* out(T) */
mangled++;
string_append (decl, "out ");
@@ -519,7 +738,7 @@ dlang_function_args (string *decl, const char *mangled)
string_append (decl, "lazy ");
break;
}
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
}
return mangled;
@@ -528,7 +747,7 @@ dlang_function_args (string *decl, const char *mangled)
/* Demangle the type from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
-dlang_type (string *decl, const char *mangled)
+dlang_type (string *decl, const char *mangled, struct dlang_info *info)
{
if (mangled == NULL || *mangled == '\0')
return NULL;
@@ -538,19 +757,19 @@ dlang_type (string *decl, const char *mangled)
case 'O': /* shared(T) */
mangled++;
string_append (decl, "shared(");
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
string_append (decl, ")");
return mangled;
case 'x': /* const(T) */
mangled++;
string_append (decl, "const(");
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
string_append (decl, ")");
return mangled;
case 'y': /* immutable(T) */
mangled++;
string_append (decl, "immutable(");
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
string_append (decl, ")");
return mangled;
case 'N':
@@ -559,7 +778,7 @@ dlang_type (string *decl, const char *mangled)
{
mangled++;
string_append (decl, "inout(");
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
string_append (decl, ")");
return mangled;
}
@@ -567,15 +786,21 @@ dlang_type (string *decl, const char *mangled)
{
mangled++;
string_append (decl, "__vector(");
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
string_append (decl, ")");
return mangled;
}
+ else if (*mangled == 'n') /* typeof(*null) */
+ {
+ mangled++;
+ string_append (decl, "typeof(*null)");
+ return mangled;
+ }
else
return NULL;
case 'A': /* dynamic array (T[]) */
mangled++;
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
string_append (decl, "[]");
return mangled;
case 'G': /* static array (T[N]) */
@@ -590,7 +815,7 @@ dlang_type (string *decl, const char *mangled)
num++;
mangled++;
}
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
string_append (decl, "[");
string_appendn (decl, numptr, num);
string_append (decl, "]");
@@ -603,10 +828,10 @@ dlang_type (string *decl, const char *mangled)
mangled++;
string_init (&type);
- mangled = dlang_type (&type, mangled);
+ mangled = dlang_type (&type, mangled, info);
sztype = string_length (&type);
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
string_append (decl, "[");
string_appendn (decl, type.b, sztype);
string_append (decl, "]");
@@ -618,7 +843,7 @@ dlang_type (string *decl, const char *mangled)
mangled++;
if (!dlang_call_convention_p (mangled))
{
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
string_append (decl, "*");
return mangled;
}
@@ -630,16 +855,15 @@ dlang_type (string *decl, const char *mangled)
case 'R': /* function T (C++) */
case 'Y': /* function T (Objective-C) */
/* Function pointer types don't include the trailing asterisk. */
- mangled = dlang_function_type (decl, mangled);
+ mangled = dlang_function_type (decl, mangled, info);
string_append (decl, "function");
return mangled;
- case 'I': /* ident T */
case 'C': /* class T */
case 'S': /* struct T */
case 'E': /* enum T */
case 'T': /* typedef T */
mangled++;
- return dlang_parse_qualified (decl, mangled, dlang_type_name);
+ return dlang_parse_qualified (decl, mangled, info, 0);
case 'D': /* delegate T */
{
string mods;
@@ -650,7 +874,12 @@ dlang_type (string *decl, const char *mangled)
mangled = dlang_type_modifiers (&mods, mangled);
szmods = string_length (&mods);
- mangled = dlang_function_type (decl, mangled);
+ /* Back referenced function type. */
+ if (mangled && *mangled == 'Q')
+ mangled = dlang_type_backref (decl, mangled, info, 1);
+ else
+ mangled = dlang_function_type (decl, mangled, info);
+
string_append (decl, "delegate");
string_appendn (decl, mods.b, szmods);
@@ -659,12 +888,12 @@ dlang_type (string *decl, const char *mangled)
}
case 'B': /* tuple T */
mangled++;
- return dlang_parse_tuple (decl, mangled);
+ return dlang_parse_tuple (decl, mangled, info);
/* Basic types */
case 'n':
mangled++;
- string_append (decl, "none");
+ string_append (decl, "typeof(null)");
return mangled;
case 'v':
mangled++;
@@ -773,6 +1002,10 @@ dlang_type (string *decl, const char *mangled)
}
return NULL;
+ /* Back referenced type. */
+ case 'Q':
+ return dlang_type_backref (decl, mangled, info, 0);
+
default: /* unhandled */
return NULL;
}
@@ -781,152 +1014,146 @@ dlang_type (string *decl, const char *mangled)
/* Extract the identifier from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
-dlang_identifier (string *decl, const char *mangled,
- enum dlang_symbol_kinds kind)
+dlang_identifier (string *decl, const char *mangled, struct dlang_info *info)
{
- long len;
+ unsigned long len;
+
+ if (mangled == NULL || *mangled == '\0')
+ return NULL;
+
+ if (*mangled == 'Q')
+ return dlang_symbol_backref (decl, mangled, info);
+
+ /* May be a template instance without a length prefix. */
+ if (mangled[0] == '_' && mangled[1] == '_'
+ && (mangled[2] == 'T' || mangled[2] == 'U'))
+ return dlang_parse_template (decl, mangled, info, TEMPLATE_LENGTH_UNKNOWN);
+
const char *endptr = dlang_number (mangled, &len);
if (endptr == NULL || len == 0)
return NULL;
- /* In template parameter symbols, the first character of the mangled
- name can be a digit. This causes ambiguity issues because the
- digits of the two numbers are adjacent. */
- if (kind == dlang_template_param)
- {
- long psize = len;
- const char *pend;
- int saved = string_length (decl);
-
- /* Work backwards until a match is found. */
- for (pend = endptr; endptr != NULL; pend--)
- {
- mangled = pend;
+ if (strlen (endptr) < len)
+ return NULL;
- /* Reached the beginning of the pointer to the name length,
- try parsing the entire symbol. */
- if (psize == 0)
- {
- psize = len;
- pend = endptr;
- endptr = NULL;
- }
+ mangled = endptr;
- /* Check whether template parameter is a function with a valid
- return type or an untyped identifier. */
- if (ISDIGIT (*mangled))
- mangled = dlang_parse_qualified (decl, mangled,
- dlang_template_ident);
- else if (strncmp (mangled, "_D", 2) == 0)
- mangled = dlang_parse_mangle (decl, mangled, dlang_function);
+ /* May be a template instance with a length prefix. */
+ if (len >= 5 && mangled[0] == '_' && mangled[1] == '_'
+ && (mangled[2] == 'T' || mangled[2] == 'U'))
+ return dlang_parse_template (decl, mangled, info, len);
- /* Check for name length mismatch. */
- if (mangled && (mangled - pend) == psize)
- return mangled;
+ /* There can be multiple different declarations in the same function that have
+ the same mangled name. To make the mangled names unique, a fake parent in
+ the form `__Sddd' is added to the symbol. */
+ if (len >= 4 && mangled[0] == '_' && mangled[1] == '_' && mangled[2] == 'S')
+ {
+ const char *numptr = mangled + 3;
+ while (numptr < (mangled + len) && ISDIGIT (*numptr))
+ numptr++;
- psize /= 10;
- string_setlength (decl, saved);
+ if (mangled + len == numptr)
+ {
+ /* Skip over the fake parent. */
+ mangled += len;
+ return dlang_identifier (decl, mangled, info);
}
- /* No match on any combinations. */
- return NULL;
+ /* else demangle it as a plain identifier. */
}
- else
- {
- if (strlen (endptr) < (size_t) len)
- return NULL;
-
- mangled = endptr;
- /* May be a template instance. */
- if (len >= 5 && mangled[0] == '_' && mangled[1] == '_'
- && (mangled[2] == 'T' || mangled[2] == 'U'))
- return dlang_parse_template (decl, mangled, len);
+ return dlang_lname (decl, mangled, len);
+}
- switch (len)
+/* Extract the plain identifier from MANGLED and prepend/append it to DECL
+ with special treatment for some magic compiler generted symbols.
+ Return the remaining string on success or NULL on failure. */
+static const char *
+dlang_lname (string *decl, const char *mangled, unsigned long len)
+{
+ switch (len)
+ {
+ case 6:
+ if (strncmp (mangled, "__ctor", len) == 0)
{
- case 6:
- if (strncmp (mangled, "__ctor", len) == 0)
- {
- /* Constructor symbol for a class/struct. */
- string_append (decl, "this");
- mangled += len;
- return mangled;
- }
- else if (strncmp (mangled, "__dtor", len) == 0)
- {
- /* Destructor symbol for a class/struct. */
- string_append (decl, "~this");
- mangled += len;
- return mangled;
- }
- else if (strncmp (mangled, "__initZ", len+1) == 0)
- {
- /* The static initialiser for a given symbol. */
- string_prepend (decl, "initializer for ");
- string_setlength (decl, string_length (decl) - 1);
- mangled += len;
- return mangled;
- }
- else if (strncmp (mangled, "__vtblZ", len+1) == 0)
- {
- /* The vtable symbol for a given class. */
- string_prepend (decl, "vtable for ");
- string_setlength (decl, string_length (decl) - 1);
- mangled += len;
- return mangled;
- }
- break;
-
- case 7:
- if (strncmp (mangled, "__ClassZ", len+1) == 0)
- {
- /* The classinfo symbol for a given class. */
- string_prepend (decl, "ClassInfo for ");
- string_setlength (decl, string_length (decl) - 1);
- mangled += len;
- return mangled;
- }
- break;
+ /* Constructor symbol for a class/struct. */
+ string_append (decl, "this");
+ mangled += len;
+ return mangled;
+ }
+ else if (strncmp (mangled, "__dtor", len) == 0)
+ {
+ /* Destructor symbol for a class/struct. */
+ string_append (decl, "~this");
+ mangled += len;
+ return mangled;
+ }
+ else if (strncmp (mangled, "__initZ", len + 1) == 0)
+ {
+ /* The static initialiser for a given symbol. */
+ string_prepend (decl, "initializer for ");
+ string_setlength (decl, string_length (decl) - 1);
+ mangled += len;
+ return mangled;
+ }
+ else if (strncmp (mangled, "__vtblZ", len + 1) == 0)
+ {
+ /* The vtable symbol for a given class. */
+ string_prepend (decl, "vtable for ");
+ string_setlength (decl, string_length (decl) - 1);
+ mangled += len;
+ return mangled;
+ }
+ break;
- case 10:
- if (strncmp (mangled, "__postblitMFZ", len+3) == 0)
- {
- /* Postblit symbol for a struct. */
- string_append (decl, "this(this)");
- mangled += len + 3;
- return mangled;
- }
- break;
+ case 7:
+ if (strncmp (mangled, "__ClassZ", len + 1) == 0)
+ {
+ /* The classinfo symbol for a given class. */
+ string_prepend (decl, "ClassInfo for ");
+ string_setlength (decl, string_length (decl) - 1);
+ mangled += len;
+ return mangled;
+ }
+ break;
- case 11:
- if (strncmp (mangled, "__InterfaceZ", len+1) == 0)
- {
- /* The interface symbol for a given class. */
- string_prepend (decl, "Interface for ");
- string_setlength (decl, string_length (decl) - 1);
- mangled += len;
- return mangled;
- }
- break;
+ case 10:
+ if (strncmp (mangled, "__postblitMFZ", len + 3) == 0)
+ {
+ /* Postblit symbol for a struct. */
+ string_append (decl, "this(this)");
+ mangled += len + 3;
+ return mangled;
+ }
+ break;
- case 12:
- if (strncmp (mangled, "__ModuleInfoZ", len+1) == 0)
- {
- /* The ModuleInfo symbol for a given module. */
- string_prepend (decl, "ModuleInfo for ");
- string_setlength (decl, string_length (decl) - 1);
- mangled += len;
- return mangled;
- }
- break;
+ case 11:
+ if (strncmp (mangled, "__InterfaceZ", len + 1) == 0)
+ {
+ /* The interface symbol for a given class. */
+ string_prepend (decl, "Interface for ");
+ string_setlength (decl, string_length (decl) - 1);
+ mangled += len;
+ return mangled;
}
+ break;
- string_appendn (decl, mangled, len);
- mangled += len;
+ case 12:
+ if (strncmp (mangled, "__ModuleInfoZ", len + 1) == 0)
+ {
+ /* The ModuleInfo symbol for a given module. */
+ string_prepend (decl, "ModuleInfo for ");
+ string_setlength (decl, string_length (decl) - 1);
+ mangled += len;
+ return mangled;
+ }
+ break;
}
+ string_appendn (decl, mangled, len);
+ mangled += len;
+
return mangled;
}
@@ -939,10 +1166,10 @@ dlang_parse_integer (string *decl, const char *mangled, char type)
if (type == 'a' || type == 'u' || type == 'w')
{
/* Parse character value. */
- char value[10];
- int pos = 10;
+ char value[20];
+ int pos = sizeof(value);
int width = 0;
- long val;
+ unsigned long val;
mangled = dlang_number (mangled, &val);
if (mangled == NULL)
@@ -991,14 +1218,14 @@ dlang_parse_integer (string *decl, const char *mangled, char type)
for (; width > 0; width--)
value[--pos] = '0';
- string_appendn (decl, &(value[pos]), 10 - pos);
+ string_appendn (decl, &(value[pos]), sizeof(value) - pos);
}
string_append (decl, "'");
}
else if (type == 'b')
{
/* Parse boolean value. */
- long val;
+ unsigned long val;
mangled = dlang_number (mangled, &val);
if (mangled == NULL)
@@ -1117,7 +1344,7 @@ static const char *
dlang_parse_string (string *decl, const char *mangled)
{
char type = *mangled;
- long len;
+ unsigned long len;
mangled++;
mangled = dlang_number (mangled, &len);
@@ -1179,9 +1406,10 @@ dlang_parse_string (string *decl, const char *mangled)
/* Extract the static array value from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
-dlang_parse_arrayliteral (string *decl, const char *mangled)
+dlang_parse_arrayliteral (string *decl, const char *mangled,
+ struct dlang_info *info)
{
- long elements;
+ unsigned long elements;
mangled = dlang_number (mangled, &elements);
if (mangled == NULL)
@@ -1190,7 +1418,10 @@ dlang_parse_arrayliteral (string *decl, const char *mangled)
string_append (decl, "[");
while (elements--)
{
- mangled = dlang_value (decl, mangled, NULL, '\0');
+ mangled = dlang_value (decl, mangled, NULL, '\0', info);
+ if (mangled == NULL)
+ return NULL;
+
if (elements != 0)
string_append (decl, ", ");
}
@@ -1202,9 +1433,10 @@ dlang_parse_arrayliteral (string *decl, const char *mangled)
/* Extract the associative array value from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
-dlang_parse_assocarray (string *decl, const char *mangled)
+dlang_parse_assocarray (string *decl, const char *mangled,
+ struct dlang_info *info)
{
- long elements;
+ unsigned long elements;
mangled = dlang_number (mangled, &elements);
if (mangled == NULL)
@@ -1213,9 +1445,14 @@ dlang_parse_assocarray (string *decl, const char *mangled)
string_append (decl, "[");
while (elements--)
{
- mangled = dlang_value (decl, mangled, NULL, '\0');
+ mangled = dlang_value (decl, mangled, NULL, '\0', info);
+ if (mangled == NULL)
+ return NULL;
+
string_append (decl, ":");
- mangled = dlang_value (decl, mangled, NULL, '\0');
+ mangled = dlang_value (decl, mangled, NULL, '\0', info);
+ if (mangled == NULL)
+ return NULL;
if (elements != 0)
string_append (decl, ", ");
@@ -1228,9 +1465,10 @@ dlang_parse_assocarray (string *decl, const char *mangled)
/* Extract the struct literal value for NAME from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
-dlang_parse_structlit (string *decl, const char *mangled, const char *name)
+dlang_parse_structlit (string *decl, const char *mangled, const char *name,
+ struct dlang_info *info)
{
- long args;
+ unsigned long args;
mangled = dlang_number (mangled, &args);
if (mangled == NULL)
@@ -1242,7 +1480,10 @@ dlang_parse_structlit (string *decl, const char *mangled, const char *name)
string_append (decl, "(");
while (args--)
{
- mangled = dlang_value (decl, mangled, NULL, '\0');
+ mangled = dlang_value (decl, mangled, NULL, '\0', info);
+ if (mangled == NULL)
+ return NULL;
+
if (args != 0)
string_append (decl, ", ");
}
@@ -1254,7 +1495,8 @@ dlang_parse_structlit (string *decl, const char *mangled, const char *name)
/* Extract the value from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
-dlang_value (string *decl, const char *mangled, const char *name, char type)
+dlang_value (string *decl, const char *mangled, const char *name, char type,
+ struct dlang_info *info)
{
if (mangled == NULL || *mangled == '\0')
return NULL;
@@ -1315,15 +1557,24 @@ dlang_value (string *decl, const char *mangled, const char *name, char type)
case 'A':
mangled++;
if (type == 'H')
- mangled = dlang_parse_assocarray (decl, mangled);
+ mangled = dlang_parse_assocarray (decl, mangled, info);
else
- mangled = dlang_parse_arrayliteral (decl, mangled);
+ mangled = dlang_parse_arrayliteral (decl, mangled, info);
break;
/* Struct values. */
case 'S':
mangled++;
- mangled = dlang_parse_structlit (decl, mangled, name);
+ mangled = dlang_parse_structlit (decl, mangled, name, info);
+ break;
+
+ /* Function literal symbol. */
+ case 'f':
+ mangled++;
+ if (strncmp (mangled, "_D", 2) != 0
+ || !dlang_symbol_name_p (mangled + 2, info))
+ return NULL;
+ mangled = dlang_parse_mangle (decl, mangled, info);
break;
default:
@@ -1336,22 +1587,22 @@ dlang_value (string *decl, const char *mangled, const char *name, char type)
/* Extract and demangle the symbol in MANGLED and append it to DECL.
Returns the remaining signature on success or NULL on failure. */
static const char *
-dlang_parse_mangle (string *decl, const char *mangled,
- enum dlang_symbol_kinds kind)
+dlang_parse_mangle (string *decl, const char *mangled, struct dlang_info *info)
{
/* A D mangled symbol is comprised of both scope and type information.
MangleName:
_D QualifiedName Type
- _D QualifiedName M Type
_D QualifiedName Z
^
The caller should have guaranteed that the start pointer is at the
above location.
+ Note that type is never a function type, but only the return type of
+ a function or the type of a variable.
*/
mangled += 2;
- mangled = dlang_parse_qualified (decl, mangled, dlang_top_level);
+ mangled = dlang_parse_qualified (decl, mangled, info, 1);
if (mangled != NULL)
{
@@ -1360,123 +1611,97 @@ dlang_parse_mangle (string *decl, const char *mangled,
mangled++;
else
{
- string mods;
- int saved;
-
- /* Skip over 'this' parameter. */
- if (*mangled == 'M')
- mangled++;
-
- /* Save the type modifiers for appending at the end if needed. */
- string_init (&mods);
- mangled = dlang_type_modifiers (&mods, mangled);
-
- if (mangled && dlang_call_convention_p (mangled))
- {
- /* Skip over calling convention and attributes. */
- saved = string_length (decl);
- mangled = dlang_call_convention (decl, mangled);
- mangled = dlang_attributes (decl, mangled);
- string_setlength (decl, saved);
-
- string_append (decl, "(");
- mangled = dlang_function_args (decl, mangled);
- string_append (decl, ")");
-
- /* Add any const/immutable/shared modifier. */
- string_appendn (decl, mods.b, string_length (&mods));
- }
+ /* Discard the declaration or return type. */
+ string type;
- /* Consume the decl type of symbol. */
- saved = string_length (decl);
- mangled = dlang_type (decl, mangled);
- string_setlength (decl, saved);
-
- string_delete (&mods);
+ string_init (&type);
+ mangled = dlang_type (&type, mangled, info);
+ string_delete (&type);
}
}
- /* Check that the entire symbol was successfully demangled. */
- if (kind == dlang_top_level)
- {
- if (mangled == NULL || *mangled != '\0')
- return NULL;
- }
-
return mangled;
}
/* Extract and demangle the qualified symbol in MANGLED and append it to DECL.
+ SUFFIX_MODIFIERS is 1 if we are printing modifiers on this after the symbol.
Returns the remaining signature on success or NULL on failure. */
static const char *
dlang_parse_qualified (string *decl, const char *mangled,
- enum dlang_symbol_kinds kind)
+ struct dlang_info *info, int suffix_modifiers)
{
/* Qualified names are identifiers separated by their encoded length.
Nested functions also encode their argument types without specifying
what they return.
QualifiedName:
- SymbolName
- SymbolName QualifiedName
- SymbolName TypeFunctionNoReturn QualifiedName
- SymbolName M TypeModifiers TypeFunctionNoReturn QualifiedName
+ SymbolFunctionName
+ SymbolFunctionName QualifiedName
^
+
+ SymbolFunctionName:
+ SymbolName
+ SymbolName TypeFunctionNoReturn
+ SymbolName M TypeFunctionNoReturn
+ SymbolName M TypeModifiers TypeFunctionNoReturn
+
The start pointer should be at the above location.
*/
size_t n = 0;
do
{
+ /* Skip over anonymous symbols. */
+ if (*mangled == '0')
+ {
+ do
+ mangled++;
+ while (*mangled == '0');
+
+ continue;
+ }
+
if (n++)
string_append (decl, ".");
- /* Skip over anonymous symbols. */
- while (*mangled == '0')
- mangled++;
-
- mangled = dlang_identifier (decl, mangled, kind);
+ mangled = dlang_identifier (decl, mangled, info);
/* Consume the encoded arguments. However if this is not followed by the
- next encoded length, then this is not a continuation of a qualified
- name, in which case we backtrack and return the current unconsumed
- position of the mangled decl. */
+ next encoded length or mangle type, then this is not a continuation of
+ a qualified name, in which case we backtrack and return the current
+ unconsumed position of the mangled decl. */
if (mangled && (*mangled == 'M' || dlang_call_convention_p (mangled)))
{
+ string mods;
const char *start = mangled;
int saved = string_length (decl);
+ /* Save the type modifiers for appending at the end if needed. */
+ string_init (&mods);
+
/* Skip over 'this' parameter and type modifiers. */
if (*mangled == 'M')
{
mangled++;
- mangled = dlang_type_modifiers (decl, mangled);
+ mangled = dlang_type_modifiers (&mods, mangled);
string_setlength (decl, saved);
}
- /* The rule we expect to match in the mangled string is:
-
- TypeFunctionNoReturn:
- CallConvention FuncAttrs Arguments ArgClose
-
- The calling convention and function attributes are not included
- in the demangled string. */
- mangled = dlang_call_convention (decl, mangled);
- mangled = dlang_attributes (decl, mangled);
- string_setlength (decl, saved);
+ mangled = dlang_function_type_noreturn (decl, NULL, NULL,
+ mangled, info);
+ if (suffix_modifiers)
+ string_appendn (decl, mods.b, string_length (&mods));
- string_append (decl, "(");
- mangled = dlang_function_args (decl, mangled);
- string_append (decl, ")");
-
- if (mangled == NULL || !ISDIGIT (*mangled))
+ if (mangled == NULL || *mangled == '\0')
{
/* Did not match the rule we were looking for. */
mangled = start;
string_setlength (decl, saved);
}
+
+ string_delete (&mods);
}
}
- while (mangled && ISDIGIT (*mangled));
+ while (mangled && dlang_symbol_name_p (mangled, info));
return mangled;
}
@@ -1484,9 +1709,9 @@ dlang_parse_qualified (string *decl, const char *mangled,
/* Demangle the tuple from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
-dlang_parse_tuple (string *decl, const char *mangled)
+dlang_parse_tuple (string *decl, const char *mangled, struct dlang_info *info)
{
- long elements;
+ unsigned long elements;
mangled = dlang_number (mangled, &elements);
if (mangled == NULL)
@@ -1496,7 +1721,10 @@ dlang_parse_tuple (string *decl, const char *mangled)
while (elements--)
{
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
+ if (mangled == NULL)
+ return NULL;
+
if (elements != 0)
string_append (decl, ", ");
}
@@ -1505,10 +1733,71 @@ dlang_parse_tuple (string *decl, const char *mangled)
return mangled;
}
+/* Demangle the template symbol parameter from MANGLED and append it to DECL.
+ Return the remaining string on success or NULL on failure. */
+static const char *
+dlang_template_symbol_param (string *decl, const char *mangled,
+ struct dlang_info *info)
+{
+ if (strncmp (mangled, "_D", 2) == 0
+ && dlang_symbol_name_p (mangled + 2, info))
+ return dlang_parse_mangle (decl, mangled, info);
+
+ if (*mangled == 'Q')
+ return dlang_parse_qualified (decl, mangled, info, 0);
+
+ unsigned long len;
+ const char *endptr = dlang_number (mangled, &len);
+
+ if (endptr == NULL || len == 0)
+ return NULL;
+
+ /* In template parameter symbols generated by the frontend up to 2.076,
+ the symbol length is encoded and the first character of the mangled
+ name can be a digit. This causes ambiguity issues because the digits
+ of the two numbers are adjacent. */
+ long psize = len;
+ const char *pend;
+ int saved = string_length (decl);
+
+ /* Work backwards until a match is found. */
+ for (pend = endptr; endptr != NULL; pend--)
+ {
+ mangled = pend;
+
+ /* Reached the beginning of the pointer to the name length,
+ try parsing the entire symbol. */
+ if (psize == 0)
+ {
+ psize = len;
+ pend = endptr;
+ endptr = NULL;
+ }
+
+ /* Check whether template parameter is a function with a valid
+ return type or an untyped identifier. */
+ if (dlang_symbol_name_p (mangled, info))
+ mangled = dlang_parse_qualified (decl, mangled, info, 0);
+ else if (strncmp (mangled, "_D", 2) == 0
+ && dlang_symbol_name_p (mangled + 2, info))
+ mangled = dlang_parse_mangle (decl, mangled, info);
+
+ /* Check for name length mismatch. */
+ if (mangled && (endptr == NULL || (mangled - pend) == psize))
+ return mangled;
+
+ psize /= 10;
+ string_setlength (decl, saved);
+ }
+
+ /* No match on any combinations. */
+ return NULL;
+}
+
/* Demangle the argument list from MANGLED and append it to DECL.
Return the remaining string on success or NULL on failure. */
static const char *
-dlang_template_args (string *decl, const char *mangled)
+dlang_template_args (string *decl, const char *mangled, struct dlang_info *info)
{
size_t n = 0;
@@ -1532,11 +1821,11 @@ dlang_template_args (string *decl, const char *mangled)
{
case 'S': /* Symbol parameter. */
mangled++;
- mangled = dlang_identifier (decl, mangled, dlang_template_param);
+ mangled = dlang_template_symbol_param (decl, mangled, info);
break;
case 'T': /* Type parameter. */
mangled++;
- mangled = dlang_type (decl, mangled);
+ mangled = dlang_type (decl, mangled, info);
break;
case 'V': /* Value parameter. */
{
@@ -1547,18 +1836,41 @@ dlang_template_args (string *decl, const char *mangled)
mangled++;
type = *mangled;
+ if (type == 'Q')
+ {
+ /* Value type is a back reference, peek at the real type. */
+ const char *backref;
+ if (dlang_backref (mangled, &backref, info) == NULL)
+ return NULL;
+
+ type = *backref;
+ }
+
/* In the few instances where the type is actually desired in
the output, it should precede the value from dlang_value. */
string_init (&name);
- mangled = dlang_type (&name, mangled);
+ mangled = dlang_type (&name, mangled, info);
string_need (&name, 1);
*(name.p) = '\0';
- mangled = dlang_value (decl, mangled, name.b, type);
+ mangled = dlang_value (decl, mangled, name.b, type, info);
string_delete (&name);
break;
}
+ case 'X': /* Externally mangled parameter. */
+ {
+ unsigned long len;
+ const char *endptr;
+
+ mangled++;
+ endptr = dlang_number (mangled, &len);
+ if (endptr == NULL || strlen (endptr) < len)
+ return NULL;
+ string_appendn (decl, endptr, len);
+ mangled = endptr + len;
+ break;
+ }
default:
return NULL;
}
@@ -1568,12 +1880,14 @@ dlang_template_args (string *decl, const char *mangled)
}
/* Extract and demangle the template symbol in MANGLED, expected to
- be made up of LEN characters, and append it to DECL.
+ be made up of LEN characters (-1 if unknown), and append it to DECL.
Returns the remaining signature on success or NULL on failure. */
static const char *
-dlang_parse_template (string *decl, const char *mangled, long len)
+dlang_parse_template (string *decl, const char *mangled,
+ struct dlang_info *info, unsigned long len)
{
const char *start = mangled;
+ string args;
/* Template instance names have the types and values of its parameters
encoded into it.
@@ -1587,26 +1901,42 @@ dlang_parse_template (string *decl, const char *mangled, long len)
*/
/* Template symbol. */
- if (!ISDIGIT (mangled[3]) || mangled[3] == '0')
+ if (!dlang_symbol_name_p (mangled + 3, info) || mangled[3] == '0')
return NULL;
mangled += 3;
/* Template identifier. */
- mangled = dlang_identifier (decl, mangled, dlang_template_ident);
+ mangled = dlang_identifier (decl, mangled, info);
/* Template arguments. */
+ string_init (&args);
+ mangled = dlang_template_args (&args, mangled, info);
+
string_append (decl, "!(");
- mangled = dlang_template_args (decl, mangled);
+ string_appendn (decl, args.b, string_length (&args));
string_append (decl, ")");
+ string_delete (&args);
+
/* Check for template name length mismatch. */
- if (mangled && (mangled - start) != len)
+ if (len != TEMPLATE_LENGTH_UNKNOWN
+ && mangled
+ && (unsigned long) (mangled - start) != len)
return NULL;
return mangled;
}
+/* Initialize the information structure we use to pass around information. */
+static void
+dlang_demangle_init_info (const char *mangled, int last_backref,
+ struct dlang_info *info)
+{
+ info->s = mangled;
+ info->last_backref = last_backref;
+}
+
/* Extract and demangle the symbol in MANGLED. Returns the demangled
signature on success or NULL on failure. */
@@ -1630,7 +1960,13 @@ dlang_demangle (const char *mangled, int option ATTRIBUTE_UNUSED)
}
else
{
- if (dlang_parse_mangle (&decl, mangled, dlang_top_level) == NULL)
+ struct dlang_info info;
+
+ dlang_demangle_init_info (mangled, strlen (mangled), &info);
+ mangled = dlang_parse_mangle (&decl, mangled, &info);
+
+ /* Check that the entire symbol was successfully demangled. */
+ if (mangled == NULL || *mangled != '\0')
string_delete (&decl);
}
diff --git a/rtemstoolkit/libiberty/demangle.h b/rtemstoolkit/libiberty/demangle.h
index 996203b..f062d77 100644
--- a/rtemstoolkit/libiberty/demangle.h
+++ b/rtemstoolkit/libiberty/demangle.h
@@ -1,5 +1,5 @@
/* Defs for interface to demanglers.
- Copyright (C) 1992-2017 Free Software Foundation, Inc.
+ Copyright (C) 1992-2023 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License
@@ -53,21 +53,25 @@ extern "C" {
*/
#define DMGL_AUTO (1 << 8)
-#define DMGL_GNU (1 << 9)
-#define DMGL_LUCID (1 << 10)
-#define DMGL_ARM (1 << 11)
-#define DMGL_HP (1 << 12) /* For the HP aCC compiler;
- same as ARM except for
- template arguments, etc. */
-#define DMGL_EDG (1 << 13)
#define DMGL_GNU_V3 (1 << 14)
#define DMGL_GNAT (1 << 15)
#define DMGL_DLANG (1 << 16)
#define DMGL_RUST (1 << 17) /* Rust wraps GNU_V3 style mangling. */
/* If none of these are set, use 'current_demangling_style' as the default. */
-#define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU|DMGL_LUCID|DMGL_ARM|DMGL_HP|DMGL_EDG|DMGL_GNU_V3|DMGL_JAVA|DMGL_GNAT|DMGL_DLANG|DMGL_RUST)
-
+#define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU_V3|DMGL_JAVA|DMGL_GNAT|DMGL_DLANG|DMGL_RUST)
+
+/* Disable a limit on the depth of recursion in mangled strings.
+ Note if this limit is disabled then stack exhaustion is possible when
+ demangling pathologically complicated strings. Bug reports about stack
+ exhaustion when the option is enabled will be rejected. */
+#define DMGL_NO_RECURSE_LIMIT (1 << 18)
+
+/* If DMGL_NO_RECURSE_LIMIT is not enabled, then this is the value used as
+ the maximum depth of recursion allowed. It should be enough for any
+ real-world mangled name. */
+#define DEMANGLE_RECURSION_LIMIT 2048
+
/* Enumeration of possible demangling styles.
Lucid and ARM styles are still kept logically distinct, even though
@@ -81,11 +85,6 @@ extern enum demangling_styles
no_demangling = -1,
unknown_demangling = 0,
auto_demangling = DMGL_AUTO,
- gnu_demangling = DMGL_GNU,
- lucid_demangling = DMGL_LUCID,
- arm_demangling = DMGL_ARM,
- hp_demangling = DMGL_HP,
- edg_demangling = DMGL_EDG,
gnu_v3_demangling = DMGL_GNU_V3,
java_demangling = DMGL_JAVA,
gnat_demangling = DMGL_GNAT,
@@ -97,11 +96,6 @@ extern enum demangling_styles
#define NO_DEMANGLING_STYLE_STRING "none"
#define AUTO_DEMANGLING_STYLE_STRING "auto"
-#define GNU_DEMANGLING_STYLE_STRING "gnu"
-#define LUCID_DEMANGLING_STYLE_STRING "lucid"
-#define ARM_DEMANGLING_STYLE_STRING "arm"
-#define HP_DEMANGLING_STYLE_STRING "hp"
-#define EDG_DEMANGLING_STYLE_STRING "edg"
#define GNU_V3_DEMANGLING_STYLE_STRING "gnu-v3"
#define JAVA_DEMANGLING_STYLE_STRING "java"
#define GNAT_DEMANGLING_STYLE_STRING "gnat"
@@ -112,11 +106,6 @@ extern enum demangling_styles
#define CURRENT_DEMANGLING_STYLE current_demangling_style
#define AUTO_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_AUTO)
-#define GNU_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU)
-#define LUCID_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_LUCID)
-#define ARM_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_ARM)
-#define HP_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_HP)
-#define EDG_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_EDG)
#define GNU_V3_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU_V3)
#define JAVA_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_JAVA)
#define GNAT_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNAT)
@@ -136,17 +125,8 @@ extern const struct demangler_engine
extern char *
cplus_demangle (const char *mangled, int options);
-extern int
-cplus_demangle_opname (const char *opname, char *result, int options);
-
-extern const char *
-cplus_mangle_opname (const char *opname, int options);
-
/* Note: This sets global state. FIXME if you care about multi-threading. */
-extern void
-set_cplus_marker_for_demangling (int ch);
-
extern enum demangling_styles
cplus_demangle_set_style (enum demangling_styles style);
@@ -179,24 +159,11 @@ ada_demangle (const char *mangled, int options);
extern char *
dlang_demangle (const char *mangled, int options);
-/* Returns non-zero iff MANGLED is a rust mangled symbol. MANGLED must
- already have been demangled through cplus_demangle_v3. If this function
- returns non-zero then MANGLED can be demangled (in-place) using
- RUST_DEMANGLE_SYM. */
extern int
-rust_is_mangled (const char *mangled);
-
-/* Demangles SYM (in-place) if RUST_IS_MANGLED returned non-zero for SYM.
- If RUST_IS_MANGLED returned zero for SYM then RUST_DEMANGLE_SYM might
- replace characters that cannot be demangled with '?' and might truncate
- SYM. After calling RUST_DEMANGLE_SYM SYM might be shorter, but never
- larger. */
-extern void
-rust_demangle_sym (char *sym);
-
-/* Demangles MANGLED if it was GNU_V3 and then RUST mangled, otherwise
- returns NULL. Uses CPLUS_DEMANGLE_V3, RUST_IS_MANGLED and
- RUST_DEMANGLE_SYM. Returns a new string that is owned by the caller. */
+rust_demangle_callback (const char *mangled, int options,
+ demangle_callbackref callback, void *opaque);
+
+
extern char *
rust_demangle (const char *mangled, int options);
@@ -392,6 +359,9 @@ enum demangle_component_type
template argument, and the right subtree is either NULL or
another TEMPLATE_ARGLIST node. */
DEMANGLE_COMPONENT_TEMPLATE_ARGLIST,
+ /* A template parameter object (C++20). The left subtree is the
+ corresponding template argument. */
+ DEMANGLE_COMPONENT_TPARM_OBJ,
/* An initializer list. The left subtree is either an explicit type or
NULL, and the right subtree is a DEMANGLE_COMPONENT_ARGLIST. */
DEMANGLE_COMPONENT_INITIALIZER_LIST,
@@ -438,6 +408,9 @@ enum demangle_component_type
number which involves neither modifying the mangled string nor
allocating a new copy of the literal in memory. */
DEMANGLE_COMPONENT_LITERAL_NEG,
+ /* A vendor's builtin expression. The left subtree holds the
+ expression's name, and the right subtree is a argument list. */
+ DEMANGLE_COMPONENT_VENDOR_EXPR,
/* A libgcj compiled resource. The left subtree is the name of the
resource. */
DEMANGLE_COMPONENT_JAVA_RESOURCE,
@@ -475,8 +448,28 @@ enum demangle_component_type
DEMANGLE_COMPONENT_TRANSACTION_SAFE,
/* A cloned function. */
DEMANGLE_COMPONENT_CLONE,
+ /* A member-like friend function. */
+ DEMANGLE_COMPONENT_FRIEND,
DEMANGLE_COMPONENT_NOEXCEPT,
- DEMANGLE_COMPONENT_THROW_SPEC
+ DEMANGLE_COMPONENT_THROW_SPEC,
+
+ DEMANGLE_COMPONENT_STRUCTURED_BINDING,
+
+ DEMANGLE_COMPONENT_MODULE_NAME,
+ DEMANGLE_COMPONENT_MODULE_PARTITION,
+ DEMANGLE_COMPONENT_MODULE_ENTITY,
+ DEMANGLE_COMPONENT_MODULE_INIT,
+
+ DEMANGLE_COMPONENT_TEMPLATE_HEAD,
+ DEMANGLE_COMPONENT_TEMPLATE_TYPE_PARM,
+ DEMANGLE_COMPONENT_TEMPLATE_NON_TYPE_PARM,
+ DEMANGLE_COMPONENT_TEMPLATE_TEMPLATE_PARM,
+ DEMANGLE_COMPONENT_TEMPLATE_PACK_PARM,
+
+ /* A builtin type with argument. This holds the builtin type
+ information. */
+ DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE
+
};
/* Types which are only used internally. */
@@ -498,6 +491,7 @@ struct demangle_component
Initialize to zero. Private to d_print_comp.
All other fields are final after initialization. */
int d_printing;
+ int d_counting;
union
{
@@ -562,6 +556,15 @@ struct demangle_component
const struct demangle_builtin_type_info *type;
} s_builtin;
+ /* For DEMANGLE_COMPONENT_EXTENDED_BUILTIN_TYPE. */
+ struct
+ {
+ /* Builtin type. */
+ const struct demangle_builtin_type_info *type;
+ short arg;
+ char suffix;
+ } s_extended_builtin;
+
/* For DEMANGLE_COMPONENT_SUB_STD. */
struct
{
diff --git a/rtemstoolkit/libiberty/make-temp-file.c b/rtemstoolkit/libiberty/make-temp-file.c
index 98e215d..1d2f21d 100644
--- a/rtemstoolkit/libiberty/make-temp-file.c
+++ b/rtemstoolkit/libiberty/make-temp-file.c
@@ -1,5 +1,5 @@
/* Utility to pick a temporary filename prefix.
- Copyright (C) 1996-2017 Free Software Foundation, Inc.
+ Copyright (C) 1996-2023 Free Software Foundation, Inc.
This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
@@ -37,8 +37,13 @@ Boston, MA 02110-1301, USA. */
#include <sys/file.h> /* May get R_OK, etc. on some systems. */
#endif
#if defined(_WIN32) && !defined(__CYGWIN__)
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
#ifndef R_OK
#define R_OK 4
@@ -56,7 +61,7 @@ extern int mkstemps (char *, int);
/* Name of temporary file.
mktemp requires 6 trailing X's. */
-#define TEMP_FILE "ccXXXXXX"
+#define TEMP_FILE "XXXXXX"
#define TEMP_FILE_LEN (sizeof(TEMP_FILE) - 1)
#if !defined(_WIN32) || defined(__CYGWIN__)
@@ -76,13 +81,21 @@ try_dir (const char *dir, const char *base)
return base;
if (dir != 0
&& access (dir, R_OK | W_OK | X_OK) == 0)
- return dir;
+ {
+ /* Check to make sure dir is actually a directory. */
+#ifdef S_ISDIR
+ struct stat s;
+ if (stat (dir, &s))
+ return NULL;
+ if (!S_ISDIR (s.st_mode))
+ return NULL;
+#endif
+ return dir;
+ }
return 0;
}
static const char tmp[] = { DIR_SEPARATOR, 't', 'm', 'p', 0 };
-static const char usrtmp[] =
-{ DIR_SEPARATOR, 'u', 's', 'r', DIR_SEPARATOR, 't', 'm', 'p', 0 };
static const char vartmp[] =
{ DIR_SEPARATOR, 'v', 'a', 'r', DIR_SEPARATOR, 't', 'm', 'p', 0 };
@@ -129,9 +142,8 @@ choose_tmpdir (void)
base = try_dir (P_tmpdir, base);
#endif
- /* Try /var/tmp, /usr/tmp, then /tmp. */
+ /* Try /var/tmp, then /tmp. */
base = try_dir (vartmp, base);
- base = try_dir (usrtmp, base);
base = try_dir (tmp, base);
/* If all else fails, use the current directory! */
@@ -181,25 +193,31 @@ string is @code{malloc}ed, and the temporary file has been created.
*/
char *
-make_temp_file (const char *suffix)
+make_temp_file_with_prefix (const char *prefix, const char *suffix)
{
const char *base = choose_tmpdir ();
char *temp_filename;
- int base_len, suffix_len;
+ int base_len, suffix_len, prefix_len;
int fd;
+ if (prefix == 0)
+ prefix = "cc";
+
if (suffix == 0)
suffix = "";
base_len = strlen (base);
+ prefix_len = strlen (prefix);
suffix_len = strlen (suffix);
temp_filename = XNEWVEC (char, base_len
+ TEMP_FILE_LEN
- + suffix_len + 1);
+ + suffix_len
+ + prefix_len + 1);
strcpy (temp_filename, base);
- strcpy (temp_filename + base_len, TEMP_FILE);
- strcpy (temp_filename + base_len + TEMP_FILE_LEN, suffix);
+ strcpy (temp_filename + base_len, prefix);
+ strcpy (temp_filename + base_len + prefix_len, TEMP_FILE);
+ strcpy (temp_filename + base_len + prefix_len + TEMP_FILE_LEN, suffix);
fd = mkstemps (temp_filename, suffix_len);
/* Mkstemps failed. It may be EPERM, ENOSPC etc. */
@@ -214,3 +232,9 @@ make_temp_file (const char *suffix)
abort ();
return temp_filename;
}
+
+char *
+make_temp_file (const char *suffix)
+{
+ return make_temp_file_with_prefix (NULL, suffix);
+}
diff --git a/rtemstoolkit/libiberty/mkstemps.c b/rtemstoolkit/libiberty/mkstemps.c
index 8a0effd..91eac19 100644
--- a/rtemstoolkit/libiberty/mkstemps.c
+++ b/rtemstoolkit/libiberty/mkstemps.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-2017 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2023 Free Software Foundation, Inc.
This file is derived from mkstemp.c from the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
diff --git a/rtemstoolkit/libiberty/pex-common.c b/rtemstoolkit/libiberty/pex-common.c
index 3a6c7d4..41d741d 100644
--- a/rtemstoolkit/libiberty/pex-common.c
+++ b/rtemstoolkit/libiberty/pex-common.c
@@ -1,5 +1,5 @@
/* Common code for executing a program in a sub-process.
- Copyright (C) 2005-2017 Free Software Foundation, Inc.
+ Copyright (C) 2005-2023 Free Software Foundation, Inc.
Written by Ian Lance Taylor <ian@airs.com>.
This file is part of the libiberty library.
diff --git a/rtemstoolkit/libiberty/pex-common.h b/rtemstoolkit/libiberty/pex-common.h
index cddcf6b..9d42f3c 100644
--- a/rtemstoolkit/libiberty/pex-common.h
+++ b/rtemstoolkit/libiberty/pex-common.h
@@ -1,6 +1,6 @@
/* Utilities to execute a program in a subprocess (possibly linked by pipes
with other subprocesses), and wait for it. Shared logic.
- Copyright (C) 1996-2017 Free Software Foundation, Inc.
+ Copyright (C) 1996-2023 Free Software Foundation, Inc.
This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
diff --git a/rtemstoolkit/libiberty/pex-one.c b/rtemstoolkit/libiberty/pex-one.c
index 6b38860..60b8694 100644
--- a/rtemstoolkit/libiberty/pex-one.c
+++ b/rtemstoolkit/libiberty/pex-one.c
@@ -1,5 +1,5 @@
/* Execute a program and wait for a result.
- Copyright (C) 2005-2017 Free Software Foundation, Inc.
+ Copyright (C) 2005-2023 Free Software Foundation, Inc.
This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
diff --git a/rtemstoolkit/libiberty/pex-unix.c b/rtemstoolkit/libiberty/pex-unix.c
index 0b96370..ee52789 100644
--- a/rtemstoolkit/libiberty/pex-unix.c
+++ b/rtemstoolkit/libiberty/pex-unix.c
@@ -1,7 +1,7 @@
/* Utilities to execute a program in a subprocess (possibly linked by pipes
with other subprocesses), and wait for it. Generic Unix version
(also used for UWIN and VMS).
- Copyright (C) 1996-2017 Free Software Foundation, Inc.
+ Copyright (C) 1996-2023 Free Software Foundation, Inc.
This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
@@ -58,6 +58,9 @@ extern int errno;
#ifdef HAVE_PROCESS_H
#include <process.h>
#endif
+#ifdef HAVE_SPAWN_H
+#include <spawn.h>
+#endif
#ifdef vfork /* Autoconf may define this to fork for us. */
# define VFORK_STRING "fork"
@@ -76,7 +79,7 @@ __attribute__ ((mode (SI)));
typedef __char_ptr32 *__char_ptr_char_ptr32
__attribute__ ((mode (SI)));
-/* Return a 32 bit pointer to an array of 32 bit pointers
+/* Return a 32 bit pointer to an array of 32 bit pointers
given a 64 bit pointer to an array of 64 bit pointers. */
static __char_ptr_char_ptr32
@@ -262,7 +265,7 @@ pex_wait (struct pex_obj *obj, pid_t pid, int *status, struct pex_time *time)
pt.user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
pt.user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
- if (r2.ru_utime.tv_usec < r1.ru_utime.tv_usec)
+ if (pt.user_microseconds < 0)
{
--pt.user_seconds;
pt.user_microseconds += 1000000;
@@ -270,7 +273,7 @@ pex_wait (struct pex_obj *obj, pid_t pid, int *status, struct pex_time *time)
pt.system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
pt.system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
- if (r2.ru_stime.tv_usec < r1.ru_stime.tv_usec)
+ if (pt.system_microseconds < 0)
{
--pt.system_seconds;
pt.system_microseconds += 1000000;
@@ -298,8 +301,6 @@ pex_wait (struct pex_obj *obj, pid_t pid, int *status, struct pex_time *time)
#endif /* ! defined (HAVE_WAITPID) */
#endif /* ! defined (HAVE_WAIT4) */
-static void pex_child_error (struct pex_obj *, const char *, const char *, int)
- ATTRIBUTE_NORETURN;
static int pex_unix_open_read (struct pex_obj *, const char *, int);
static int pex_unix_open_write (struct pex_obj *, const char *, int, int);
static pid_t pex_unix_exec_child (struct pex_obj *, int, const char *,
@@ -366,28 +367,6 @@ pex_unix_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
return close (fd);
}
-/* Report an error from a child process. We don't use stdio routines,
- because we might be here due to a vfork call. */
-
-static void
-pex_child_error (struct pex_obj *obj, const char *executable,
- const char *errmsg, int err)
-{
- int retval = 0;
-#define writeerr(s) retval |= (write (STDERR_FILE_NO, s, strlen (s)) < 0)
- writeerr (obj->pname);
- writeerr (": error trying to exec '");
- writeerr (executable);
- writeerr ("': ");
- writeerr (errmsg);
- writeerr (": ");
- writeerr (xstrerror (err));
- writeerr ("\n");
-#undef writeerr
- /* Exit with -2 if the error output failed, too. */
- _exit (retval == 0 ? -1 : -2);
-}
-
/* Execute a child. */
#if defined(HAVE_SPAWNVE) && defined(HAVE_SPAWNVPE)
@@ -583,6 +562,171 @@ pex_unix_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED,
return (pid_t) -1;
}
+#elif defined(HAVE_POSIX_SPAWN) && defined(HAVE_POSIX_SPAWNP)
+/* Implementation of pex->exec_child using posix_spawn. */
+
+static pid_t
+pex_unix_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED,
+ int flags, const char *executable,
+ char * const * argv, char * const * env,
+ int in, int out, int errdes,
+ int toclose, const char **errmsg, int *err)
+{
+ int ret;
+ pid_t pid = -1;
+ posix_spawnattr_t attr;
+ posix_spawn_file_actions_t actions;
+ int attr_initialized = 0, actions_initialized = 0;
+
+ *err = 0;
+
+ ret = posix_spawnattr_init (&attr);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawnattr_init";
+ goto exit;
+ }
+ attr_initialized = 1;
+
+ /* Use vfork() on glibc <=2.24. */
+#ifdef POSIX_SPAWN_USEVFORK
+ ret = posix_spawnattr_setflags (&attr, POSIX_SPAWN_USEVFORK);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawnattr_setflags";
+ goto exit;
+ }
+#endif
+
+ ret = posix_spawn_file_actions_init (&actions);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawn_file_actions_init";
+ goto exit;
+ }
+ actions_initialized = 1;
+
+ if (in != STDIN_FILE_NO)
+ {
+ ret = posix_spawn_file_actions_adddup2 (&actions, in, STDIN_FILE_NO);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawn_file_actions_adddup2";
+ goto exit;
+ }
+
+ ret = posix_spawn_file_actions_addclose (&actions, in);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawn_file_actions_addclose";
+ goto exit;
+ }
+ }
+
+ if (out != STDOUT_FILE_NO)
+ {
+ ret = posix_spawn_file_actions_adddup2 (&actions, out, STDOUT_FILE_NO);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawn_file_actions_adddup2";
+ goto exit;
+ }
+
+ ret = posix_spawn_file_actions_addclose (&actions, out);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawn_file_actions_addclose";
+ goto exit;
+ }
+ }
+
+ if (errdes != STDERR_FILE_NO)
+ {
+ ret = posix_spawn_file_actions_adddup2 (&actions, errdes, STDERR_FILE_NO);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawn_file_actions_adddup2";
+ goto exit;
+ }
+
+ ret = posix_spawn_file_actions_addclose (&actions, errdes);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawn_file_actions_addclose";
+ goto exit;
+ }
+ }
+
+ if (toclose >= 0)
+ {
+ ret = posix_spawn_file_actions_addclose (&actions, toclose);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawn_file_actions_addclose";
+ goto exit;
+ }
+ }
+
+ if ((flags & PEX_STDERR_TO_STDOUT) != 0)
+ {
+ ret = posix_spawn_file_actions_adddup2 (&actions, STDOUT_FILE_NO, STDERR_FILE_NO);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawn_file_actions_adddup2";
+ goto exit;
+ }
+ }
+
+ if ((flags & PEX_SEARCH) != 0)
+ {
+ ret = posix_spawnp (&pid, executable, &actions, &attr, argv, env ? env : environ);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawnp";
+ goto exit;
+ }
+ }
+ else
+ {
+ ret = posix_spawn (&pid, executable, &actions, &attr, argv, env ? env : environ);
+ if (ret)
+ {
+ *err = ret;
+ *errmsg = "posix_spawn";
+ goto exit;
+ }
+ }
+
+exit:
+ if (actions_initialized)
+ posix_spawn_file_actions_destroy (&actions);
+ if (attr_initialized)
+ posix_spawnattr_destroy (&attr);
+
+ if (!*err && in != STDIN_FILE_NO)
+ if (close (in))
+ *errmsg = "close", *err = errno, pid = -1;
+ if (!*err && out != STDOUT_FILE_NO)
+ if (close (out))
+ *errmsg = "close", *err = errno, pid = -1;
+ if (!*err && errdes != STDERR_FILE_NO)
+ if (close (errdes))
+ *errmsg = "close", *err = errno, pid = -1;
+
+ return pid;
+}
#else
/* Implementation of pex->exec_child using standard vfork + exec. */
@@ -592,21 +736,53 @@ pex_unix_exec_child (struct pex_obj *obj, int flags, const char *executable,
int in, int out, int errdes,
int toclose, const char **errmsg, int *err)
{
- pid_t pid;
+ pid_t pid = -1;
+ /* Tuple to communicate error from child to parent. We can safely
+ transfer string literal pointers as both run with identical
+ address mappings. */
+ struct fn_err
+ {
+ const char *fn;
+ int err;
+ };
+ volatile int do_pipe = 0;
+ volatile int pipes[2]; /* [0]:reader,[1]:writer. */
+#ifdef O_CLOEXEC
+ do_pipe = 1;
+#endif
+ if (do_pipe)
+ {
+#ifdef HAVE_PIPE2
+ if (pipe2 ((int *)pipes, O_CLOEXEC))
+ do_pipe = 0;
+#else
+ if (pipe ((int *)pipes))
+ do_pipe = 0;
+ else
+ {
+ if (fcntl (pipes[1], F_SETFD, FD_CLOEXEC) == -1)
+ {
+ close (pipes[0]);
+ close (pipes[1]);
+ do_pipe = 0;
+ }
+ }
+#endif
+ }
/* We declare these to be volatile to avoid warnings from gcc about
them being clobbered by vfork. */
- volatile int sleep_interval;
+ volatile int sleep_interval = 1;
volatile int retries;
/* We vfork and then set environ in the child before calling execvp.
This clobbers the parent's environ so we need to restore it.
It would be nice to use one of the exec* functions that takes an
- environment as a parameter, but that may have portability issues. */
- char **save_environ = environ;
+ environment as a parameter, but that may have portability
+ issues. It is marked volatile so the child doesn't consider it a
+ dead variable and therefore clobber where ever it is stored. */
+ char **volatile save_environ = environ;
- sleep_interval = 1;
- pid = -1;
for (retries = 0; retries < 4; ++retries)
{
pid = vfork ();
@@ -619,104 +795,138 @@ pex_unix_exec_child (struct pex_obj *obj, int flags, const char *executable,
switch (pid)
{
case -1:
+ if (do_pipe)
+ {
+ close (pipes[0]);
+ close (pipes[1]);
+ }
*err = errno;
*errmsg = VFORK_STRING;
return (pid_t) -1;
case 0:
/* Child process. */
- if (in != STDIN_FILE_NO)
- {
- if (dup2 (in, STDIN_FILE_NO) < 0)
- pex_child_error (obj, executable, "dup2", errno);
- if (close (in) < 0)
- pex_child_error (obj, executable, "close", errno);
- }
- if (out != STDOUT_FILE_NO)
- {
- if (dup2 (out, STDOUT_FILE_NO) < 0)
- pex_child_error (obj, executable, "dup2", errno);
- if (close (out) < 0)
- pex_child_error (obj, executable, "close", errno);
- }
- if (errdes != STDERR_FILE_NO)
- {
- if (dup2 (errdes, STDERR_FILE_NO) < 0)
- pex_child_error (obj, executable, "dup2", errno);
- if (close (errdes) < 0)
- pex_child_error (obj, executable, "close", errno);
- }
- if (toclose >= 0)
- {
- if (close (toclose) < 0)
- pex_child_error (obj, executable, "close", errno);
- }
- if ((flags & PEX_STDERR_TO_STDOUT) != 0)
- {
- if (dup2 (STDOUT_FILE_NO, STDERR_FILE_NO) < 0)
- pex_child_error (obj, executable, "dup2", errno);
- }
-
- if (env)
- {
- /* NOTE: In a standard vfork implementation this clobbers the
- parent's copy of environ "too" (in reality there's only one copy).
- This is ok as we restore it below. */
- environ = (char**) env;
- }
-
- if ((flags & PEX_SEARCH) != 0)
- {
- execvp (executable, to_ptr32 (argv));
- pex_child_error (obj, executable, "execvp", errno);
- }
- else
- {
- execv (executable, to_ptr32 (argv));
- pex_child_error (obj, executable, "execv", errno);
- }
+ {
+ struct fn_err failed;
+ failed.fn = NULL;
+
+ if (do_pipe)
+ close (pipes[0]);
+ if (!failed.fn && in != STDIN_FILE_NO)
+ {
+ if (dup2 (in, STDIN_FILE_NO) < 0)
+ failed.fn = "dup2", failed.err = errno;
+ else if (close (in) < 0)
+ failed.fn = "close", failed.err = errno;
+ }
+ if (!failed.fn && out != STDOUT_FILE_NO)
+ {
+ if (dup2 (out, STDOUT_FILE_NO) < 0)
+ failed.fn = "dup2", failed.err = errno;
+ else if (close (out) < 0)
+ failed.fn = "close", failed.err = errno;
+ }
+ if (!failed.fn && errdes != STDERR_FILE_NO)
+ {
+ if (dup2 (errdes, STDERR_FILE_NO) < 0)
+ failed.fn = "dup2", failed.err = errno;
+ else if (close (errdes) < 0)
+ failed.fn = "close", failed.err = errno;
+ }
+ if (!failed.fn && toclose >= 0)
+ {
+ if (close (toclose) < 0)
+ failed.fn = "close", failed.err = errno;
+ }
+ if (!failed.fn && (flags & PEX_STDERR_TO_STDOUT) != 0)
+ {
+ if (dup2 (STDOUT_FILE_NO, STDERR_FILE_NO) < 0)
+ failed.fn = "dup2", failed.err = errno;
+ }
+ if (!failed.fn)
+ {
+ if (env)
+ /* NOTE: In a standard vfork implementation this clobbers
+ the parent's copy of environ "too" (in reality there's
+ only one copy). This is ok as we restore it below. */
+ environ = (char**) env;
+ if ((flags & PEX_SEARCH) != 0)
+ {
+ execvp (executable, to_ptr32 (argv));
+ failed.fn = "execvp", failed.err = errno;
+ }
+ else
+ {
+ execv (executable, to_ptr32 (argv));
+ failed.fn = "execv", failed.err = errno;
+ }
+ }
+
+ /* Something failed, report an error. We don't use stdio
+ routines, because we might be here due to a vfork call. */
+ ssize_t retval = 0;
+
+ if (!do_pipe
+ || write (pipes[1], &failed, sizeof (failed)) != sizeof (failed))
+ {
+ /* The parent will not see our scream above, so write to
+ stdout. */
+#define writeerr(s) (retval |= write (STDERR_FILE_NO, s, strlen (s)))
+ writeerr (obj->pname);
+ writeerr (": error trying to exec '");
+ writeerr (executable);
+ writeerr ("': ");
+ writeerr (failed.fn);
+ writeerr (": ");
+ writeerr (xstrerror (failed.err));
+ writeerr ("\n");
+#undef writeerr
+ }
+ /* Exit with -2 if the error output failed, too. */
+ _exit (retval < 0 ? -2 : -1);
+ }
/* NOTREACHED */
return (pid_t) -1;
default:
/* Parent process. */
-
- /* Restore environ.
- Note that the parent either doesn't run until the child execs/exits
- (standard vfork behaviour), or if it does run then vfork is behaving
- more like fork. In either case we needn't worry about clobbering
- the child's copy of environ. */
- environ = save_environ;
-
- if (in != STDIN_FILE_NO)
- {
+ {
+ /* Restore environ. Note that the parent either doesn't run
+ until the child execs/exits (standard vfork behaviour), or
+ if it does run then vfork is behaving more like fork. In
+ either case we needn't worry about clobbering the child's
+ copy of environ. */
+ environ = save_environ;
+
+ struct fn_err failed;
+ failed.fn = NULL;
+ if (do_pipe)
+ {
+ close (pipes[1]);
+ ssize_t len = read (pipes[0], &failed, sizeof (failed));
+ if (len < 0)
+ failed.fn = NULL;
+ close (pipes[0]);
+ }
+
+ if (!failed.fn && in != STDIN_FILE_NO)
if (close (in) < 0)
- {
- *err = errno;
- *errmsg = "close";
- return (pid_t) -1;
- }
- }
- if (out != STDOUT_FILE_NO)
- {
+ failed.fn = "close", failed.err = errno;
+ if (!failed.fn && out != STDOUT_FILE_NO)
if (close (out) < 0)
- {
- *err = errno;
- *errmsg = "close";
- return (pid_t) -1;
- }
- }
- if (errdes != STDERR_FILE_NO)
- {
+ failed.fn = "close", failed.err = errno;
+ if (!failed.fn && errdes != STDERR_FILE_NO)
if (close (errdes) < 0)
- {
- *err = errno;
- *errmsg = "close";
- return (pid_t) -1;
- }
- }
-
+ failed.fn = "close", failed.err = errno;
+
+ if (failed.fn)
+ {
+ *err = failed.err;
+ *errmsg = failed.fn;
+ return (pid_t) -1;
+ }
+ }
return pid;
}
}
diff --git a/rtemstoolkit/libiberty/pex-win32.c b/rtemstoolkit/libiberty/pex-win32.c
index 9131158..f7fe306 100644
--- a/rtemstoolkit/libiberty/pex-win32.c
+++ b/rtemstoolkit/libiberty/pex-win32.c
@@ -1,6 +1,6 @@
/* Utilities to execute a program in a subprocess (possibly linked by pipes
with other subprocesses), and wait for it. Generic Win32 specialization.
- Copyright (C) 1996-2017 Free Software Foundation, Inc.
+ Copyright (C) 1996-2023 Free Software Foundation, Inc.
This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
@@ -20,6 +20,7 @@ Boston, MA 02110-1301, USA. */
#include "pex-common.h"
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#ifdef HAVE_STDLIB_H
@@ -350,7 +351,7 @@ argv_to_cmdline (char *const *argv)
prevent wasting 2 chars per argument of the CreateProcess 32k char
limit. We need only escape embedded double-quotes and immediately
preceeding backslash characters. A sequence of backslach characters
- that is not follwed by a double quote character will not be
+ that is not followed by a double quote character will not be
escaped. */
needs_quotes = 0;
for (j = 0; argv[i][j]; j++)
@@ -365,7 +366,7 @@ argv_to_cmdline (char *const *argv)
/* Escape preceeding backslashes. */
for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--)
cmdline_len++;
- /* Escape the qote character. */
+ /* Escape the quote character. */
cmdline_len++;
}
}
@@ -568,7 +569,8 @@ env_compare (const void *a_ptr, const void *b_ptr)
* target is not actually an executable, such as if it is a shell script. */
static pid_t
-win32_spawn (const char *executable,
+win32_spawn (struct pex_obj *obj,
+ const char *executable,
BOOL search,
char *const *argv,
char *const *env, /* array of strings of the form: VAR=VALUE */
@@ -576,14 +578,12 @@ win32_spawn (const char *executable,
LPSTARTUPINFO si,
LPPROCESS_INFORMATION pi)
{
- char *full_executable;
- char *cmdline;
+ char *full_executable = NULL;
+ char *cmdline = NULL;
+ pid_t pid = (pid_t) -1;
char **env_copy;
char *env_block = NULL;
- full_executable = NULL;
- cmdline = NULL;
-
if (env)
{
int env_size;
@@ -621,13 +621,42 @@ win32_spawn (const char *executable,
full_executable = find_executable (executable, search);
if (!full_executable)
- goto error;
+ goto exit;
cmdline = argv_to_cmdline (argv);
if (!cmdline)
- goto error;
-
- /* Create the child process. */
- if (!CreateProcess (full_executable, cmdline,
+ goto exit;
+ /* If cmdline is too large, CreateProcess will fail with a bad
+ 'No such file or directory' error. Try passing it through a
+ temporary response file instead. */
+ if (strlen (cmdline) > 32767)
+ {
+ char *response_file = make_temp_file ("");
+ /* Register the file for deletion by pex_free. */
+ ++obj->remove_count;
+ obj->remove = XRESIZEVEC (char *, obj->remove, obj->remove_count);
+ obj->remove[obj->remove_count - 1] = response_file;
+ int fd = pex_win32_open_write (obj, response_file, 0, 0);
+ if (fd == -1)
+ goto exit;
+ FILE *f = pex_win32_fdopenw (obj, fd, 0);
+ /* Don't write argv[0] (program name) to the response file. */
+ if (writeargv (&argv[1], f))
+ {
+ fclose (f);
+ goto exit;
+ }
+ fclose (f); /* Also closes fd and the underlying OS handle. */
+ char *response_arg = concat ("@", response_file, NULL);
+ char *response_argv[3] = {argv[0], response_arg, NULL};
+ free (cmdline);
+ cmdline = argv_to_cmdline (response_argv);
+ free (response_arg);
+ if (!cmdline)
+ goto exit;
+ }
+
+ /* Create the child process. */
+ if (CreateProcess (full_executable, cmdline,
/*lpProcessAttributes=*/NULL,
/*lpThreadAttributes=*/NULL,
/*bInheritHandles=*/TRUE,
@@ -637,33 +666,25 @@ win32_spawn (const char *executable,
si,
pi))
{
- free (env_block);
-
- free (full_executable);
-
- return (pid_t) -1;
+ CloseHandle (pi->hThread);
+ pid = (pid_t) pi->hProcess;
}
+ exit:
/* Clean up. */
- CloseHandle (pi->hThread);
- free (full_executable);
- free (env_block);
-
- return (pid_t) pi->hProcess;
-
- error:
free (env_block);
free (cmdline);
free (full_executable);
-
- return (pid_t) -1;
+
+ return pid;
}
/* Spawn a script. This simulates the Unix script execution mechanism.
This function is called as a fallback if win32_spawn fails. */
static pid_t
-spawn_script (const char *executable, char *const *argv,
+spawn_script (struct pex_obj *obj,
+ const char *executable, char *const *argv,
char* const *env,
DWORD dwCreationFlags,
LPSTARTUPINFO si,
@@ -713,20 +734,20 @@ spawn_script (const char *executable, char *const *argv,
executable = strrchr (executable1, '\\') + 1;
if (!executable)
executable = executable1;
- pid = win32_spawn (executable, TRUE, argv, env,
+ pid = win32_spawn (obj, executable, TRUE, argv, env,
dwCreationFlags, si, pi);
#else
if (strchr (executable1, '\\') == NULL)
- pid = win32_spawn (executable1, TRUE, argv, env,
+ pid = win32_spawn (obj, executable1, TRUE, argv, env,
dwCreationFlags, si, pi);
else if (executable1[0] != '\\')
- pid = win32_spawn (executable1, FALSE, argv, env,
+ pid = win32_spawn (obj, executable1, FALSE, argv, env,
dwCreationFlags, si, pi);
else
{
const char *newex = mingw_rootify (executable1);
*avhere = newex;
- pid = win32_spawn (newex, FALSE, argv, env,
+ pid = win32_spawn (obj, newex, FALSE, argv, env,
dwCreationFlags, si, pi);
if (executable1 != newex)
free ((char *) newex);
@@ -736,7 +757,7 @@ spawn_script (const char *executable, char *const *argv,
if (newex != executable1)
{
*avhere = newex;
- pid = win32_spawn (newex, FALSE, argv, env,
+ pid = win32_spawn (obj, newex, FALSE, argv, env,
dwCreationFlags, si, pi);
free ((char *) newex);
}
@@ -755,7 +776,7 @@ spawn_script (const char *executable, char *const *argv,
/* Execute a child. */
static pid_t
-pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
+pex_win32_exec_child (struct pex_obj *obj, int flags,
const char *executable, char * const * argv,
char* const* env,
int in, int out, int errdes,
@@ -771,7 +792,7 @@ pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
OSVERSIONINFO version_info;
STARTUPINFO si;
PROCESS_INFORMATION pi;
- int orig_out, orig_in, orig_err;
+ int orig_out, orig_in, orig_err = 0;
BOOL separate_stderr = !(flags & PEX_STDERR_TO_STDOUT);
/* Ensure we have inheritable descriptors to pass to the child. */
@@ -851,10 +872,10 @@ pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
si.hStdError = stderr_handle;
/* Create the child process. */
- pid = win32_spawn (executable, (flags & PEX_SEARCH) != 0,
+ pid = win32_spawn (obj, executable, (flags & PEX_SEARCH) != 0,
argv, env, dwCreationFlags, &si, &pi);
if (pid == (pid_t) -1)
- pid = spawn_script (executable, argv, env, dwCreationFlags,
+ pid = spawn_script (obj, executable, argv, env, dwCreationFlags,
&si, &pi);
if (pid == (pid_t) -1)
{
diff --git a/rtemstoolkit/libiberty/rust-demangle.c b/rtemstoolkit/libiberty/rust-demangle.c
index 3d7d090..c7630f1 100644
--- a/rtemstoolkit/libiberty/rust-demangle.c
+++ b/rtemstoolkit/libiberty/rust-demangle.c
@@ -1,6 +1,7 @@
/* Demangler for the Rust programming language
- Copyright (C) 2016-2017 Free Software Foundation, Inc.
+ Copyright (C) 2016-2023 Free Software Foundation, Inc.
Written by David Tolnay (dtolnay@gmail.com).
+ Rewritten by Eduard-Mihai Burtescu (eddyb@lyken.rs) for v0 support.
This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
@@ -33,9 +34,11 @@ If not, see <http://www.gnu.org/licenses/>. */
#include "safe-ctype.h"
+#include <inttypes.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
#ifdef HAVE_STRING_H
#include <string.h>
@@ -48,301 +51,1554 @@ extern void *memset(void *s, int c, size_t n);
#include <demangle.h>
#include "libiberty.h"
+struct rust_demangler
+{
+ const char *sym;
+ size_t sym_len;
-/* Mangled Rust symbols look like this:
- _$LT$std..sys..fd..FileDesc$u20$as$u20$core..ops..Drop$GT$::drop::hc68340e1baa4987a
+ void *callback_opaque;
+ demangle_callbackref callback;
- The original symbol is:
- <std::sys::fd::FileDesc as core::ops::Drop>::drop
+ /* Position of the next character to read from the symbol. */
+ size_t next;
- The last component of the path is a 64-bit hash in lowercase hex,
- prefixed with "h". Rust does not have a global namespace between
- crates, an illusion which Rust maintains by using the hash to
- distinguish things that would otherwise have the same symbol.
+ /* Non-zero if any error occurred. */
+ int errored;
- Any path component not starting with a XID_Start character is
- prefixed with "_".
+ /* Non-zero if nothing should be printed. */
+ int skipping_printing;
- The following escape sequences are used:
+ /* Non-zero if printing should be verbose (e.g. include hashes). */
+ int verbose;
- "," => $C$
- "@" => $SP$
- "*" => $BP$
- "&" => $RF$
- "<" => $LT$
- ">" => $GT$
- "(" => $LP$
- ")" => $RP$
- " " => $u20$
- "\"" => $u22$
- "'" => $u27$
- "+" => $u2b$
- ";" => $u3b$
- "[" => $u5b$
- "]" => $u5d$
- "{" => $u7b$
- "}" => $u7d$
- "~" => $u7e$
+ /* Rust mangling version, with legacy mangling being -1. */
+ int version;
- A double ".." means "::" and a single "." means "-".
+ /* Recursion depth. */
+ unsigned int recursion;
+ /* Maximum number of times demangle_path may be called recursively. */
+#define RUST_MAX_RECURSION_COUNT 1024
+#define RUST_NO_RECURSION_LIMIT ((unsigned int) -1)
- The only characters allowed in the mangled symbol are a-zA-Z0-9 and _.:$ */
+ uint64_t bound_lifetime_depth;
+};
-static const char *hash_prefix = "::h";
-static const size_t hash_prefix_len = 3;
-static const size_t hash_len = 16;
+/* Parsing functions. */
-static int is_prefixed_hash (const char *start);
-static int looks_like_rust (const char *sym, size_t len);
-static int unescape (const char **in, char **out, const char *seq, char value);
+static char
+peek (const struct rust_demangler *rdm)
+{
+ if (rdm->next < rdm->sym_len)
+ return rdm->sym[rdm->next];
+ return 0;
+}
-/* INPUT: sym: symbol that has been through C++ (gnu v3) demangling
+static int
+eat (struct rust_demangler *rdm, char c)
+{
+ if (peek (rdm) == c)
+ {
+ rdm->next++;
+ return 1;
+ }
+ else
+ return 0;
+}
- This function looks for the following indicators:
+static char
+next (struct rust_demangler *rdm)
+{
+ char c = peek (rdm);
+ if (!c)
+ rdm->errored = 1;
+ else
+ rdm->next++;
+ return c;
+}
- 1. The hash must consist of "h" followed by 16 lowercase hex digits.
+static uint64_t
+parse_integer_62 (struct rust_demangler *rdm)
+{
+ char c;
+ uint64_t x;
- 2. As a sanity check, the hash must use between 5 and 15 of the 16
- possible hex digits. This is true of 99.9998% of hashes so once
- in your life you may see a false negative. The point is to
- notice path components that could be Rust hashes but are
- probably not, like "haaaaaaaaaaaaaaaa". In this case a false
- positive (non-Rust symbol has an important path component
- removed because it looks like a Rust hash) is worse than a false
- negative (the rare Rust symbol is not demangled) so this sets
- the balance in favor of false negatives.
+ if (eat (rdm, '_'))
+ return 0;
- 3. There must be no characters other than a-zA-Z0-9 and _.:$
+ x = 0;
+ while (!eat (rdm, '_') && !rdm->errored)
+ {
+ c = next (rdm);
+ x *= 62;
+ if (ISDIGIT (c))
+ x += c - '0';
+ else if (ISLOWER (c))
+ x += 10 + (c - 'a');
+ else if (ISUPPER (c))
+ x += 10 + 26 + (c - 'A');
+ else
+ {
+ rdm->errored = 1;
+ return 0;
+ }
+ }
+ return x + 1;
+}
- 4. There must be no unrecognized $-sign sequences.
+static uint64_t
+parse_opt_integer_62 (struct rust_demangler *rdm, char tag)
+{
+ if (!eat (rdm, tag))
+ return 0;
+ return 1 + parse_integer_62 (rdm);
+}
- 5. There must be no sequence of three or more dots in a row ("..."). */
+static uint64_t
+parse_disambiguator (struct rust_demangler *rdm)
+{
+ return parse_opt_integer_62 (rdm, 's');
+}
-int
-rust_is_mangled (const char *sym)
+static size_t
+parse_hex_nibbles (struct rust_demangler *rdm, uint64_t *value)
{
- size_t len, len_without_hash;
+ char c;
+ size_t hex_len;
- if (!sym)
- return 0;
+ hex_len = 0;
+ *value = 0;
- len = strlen (sym);
- if (len <= hash_prefix_len + hash_len)
- /* Not long enough to contain "::h" + hash + something else */
- return 0;
+ while (!eat (rdm, '_'))
+ {
+ *value <<= 4;
- len_without_hash = len - (hash_prefix_len + hash_len);
- if (!is_prefixed_hash (sym + len_without_hash))
- return 0;
+ c = next (rdm);
+ if (ISDIGIT (c))
+ *value |= c - '0';
+ else if (c >= 'a' && c <= 'f')
+ *value |= 10 + (c - 'a');
+ else
+ {
+ rdm->errored = 1;
+ return 0;
+ }
+ hex_len++;
+ }
+
+ return hex_len;
+}
+
+struct rust_mangled_ident
+{
+ /* ASCII part of the identifier. */
+ const char *ascii;
+ size_t ascii_len;
+
+ /* Punycode insertion codes for Unicode codepoints, if any. */
+ const char *punycode;
+ size_t punycode_len;
+};
+
+static struct rust_mangled_ident
+parse_ident (struct rust_demangler *rdm)
+{
+ char c;
+ size_t start, len;
+ int is_punycode = 0;
+ struct rust_mangled_ident ident;
+
+ ident.ascii = NULL;
+ ident.ascii_len = 0;
+ ident.punycode = NULL;
+ ident.punycode_len = 0;
+
+ if (rdm->version != -1)
+ is_punycode = eat (rdm, 'u');
+
+ c = next (rdm);
+ if (!ISDIGIT (c))
+ {
+ rdm->errored = 1;
+ return ident;
+ }
+ len = c - '0';
+
+ if (c != '0')
+ while (ISDIGIT (peek (rdm)))
+ len = len * 10 + (next (rdm) - '0');
+
+ /* Skip past the optional `_` separator (v0). */
+ if (rdm->version != -1)
+ eat (rdm, '_');
- return looks_like_rust (sym, len_without_hash);
+ start = rdm->next;
+ rdm->next += len;
+ /* Check for overflows. */
+ if ((start > rdm->next) || (rdm->next > rdm->sym_len))
+ {
+ rdm->errored = 1;
+ return ident;
+ }
+
+ ident.ascii = rdm->sym + start;
+ ident.ascii_len = len;
+
+ if (is_punycode)
+ {
+ ident.punycode_len = 0;
+ while (ident.ascii_len > 0)
+ {
+ ident.ascii_len--;
+
+ /* The last '_' is a separator between ascii & punycode. */
+ if (ident.ascii[ident.ascii_len] == '_')
+ break;
+
+ ident.punycode_len++;
+ }
+ if (!ident.punycode_len)
+ {
+ rdm->errored = 1;
+ return ident;
+ }
+ ident.punycode = ident.ascii + (len - ident.punycode_len);
+ }
+
+ if (ident.ascii_len == 0)
+ ident.ascii = NULL;
+
+ return ident;
}
-/* A hash is the prefix "::h" followed by 16 lowercase hex digits. The
- hex digits must comprise between 5 and 15 (inclusive) distinct
- digits. */
+/* Printing functions. */
+
+static void
+print_str (struct rust_demangler *rdm, const char *data, size_t len)
+{
+ if (!rdm->errored && !rdm->skipping_printing)
+ rdm->callback (data, len, rdm->callback_opaque);
+}
+#define PRINT(s) print_str (rdm, s, strlen (s))
+
+static void
+print_uint64 (struct rust_demangler *rdm, uint64_t x)
+{
+ char s[21];
+ snprintf (s, 21, "%" PRIu64, x);
+ PRINT (s);
+}
+
+static void
+print_uint64_hex (struct rust_demangler *rdm, uint64_t x)
+{
+ char s[17];
+ snprintf (s, 17, "%" PRIx64, x);
+ PRINT (s);
+}
+
+/* Return a 0x0-0xf value if the char is 0-9a-f, and -1 otherwise. */
static int
-is_prefixed_hash (const char *str)
+decode_lower_hex_nibble (char nibble)
{
- const char *end;
- char seen[16];
- size_t i;
- int count;
+ if ('0' <= nibble && nibble <= '9')
+ return nibble - '0';
+ if ('a' <= nibble && nibble <= 'f')
+ return 0xa + (nibble - 'a');
+ return -1;
+}
- if (strncmp (str, hash_prefix, hash_prefix_len))
+/* Return the unescaped character for a "$...$" escape, or 0 if invalid. */
+static char
+decode_legacy_escape (const char *e, size_t len, size_t *out_len)
+{
+ char c = 0;
+ size_t escape_len = 0;
+ int lo_nibble = -1, hi_nibble = -1;
+
+ if (len < 3 || e[0] != '$')
return 0;
- str += hash_prefix_len;
-
- memset (seen, 0, sizeof(seen));
- for (end = str + hash_len; str < end; str++)
- if (*str >= '0' && *str <= '9')
- seen[*str - '0'] = 1;
- else if (*str >= 'a' && *str <= 'f')
- seen[*str - 'a' + 10] = 1;
- else
- return 0;
- /* Count how many distinct digits seen */
- count = 0;
- for (i = 0; i < 16; i++)
- if (seen[i])
- count++;
+ e++;
+ len--;
+
+ if (e[0] == 'C')
+ {
+ escape_len = 1;
+
+ c = ',';
+ }
+ else if (len > 2)
+ {
+ escape_len = 2;
+
+ if (e[0] == 'S' && e[1] == 'P')
+ c = '@';
+ else if (e[0] == 'B' && e[1] == 'P')
+ c = '*';
+ else if (e[0] == 'R' && e[1] == 'F')
+ c = '&';
+ else if (e[0] == 'L' && e[1] == 'T')
+ c = '<';
+ else if (e[0] == 'G' && e[1] == 'T')
+ c = '>';
+ else if (e[0] == 'L' && e[1] == 'P')
+ c = '(';
+ else if (e[0] == 'R' && e[1] == 'P')
+ c = ')';
+ else if (e[0] == 'u' && len > 3)
+ {
+ escape_len = 3;
- return count >= 5 && count <= 15;
+ hi_nibble = decode_lower_hex_nibble (e[1]);
+ if (hi_nibble < 0)
+ return 0;
+ lo_nibble = decode_lower_hex_nibble (e[2]);
+ if (lo_nibble < 0)
+ return 0;
+
+ /* Only allow non-control ASCII characters. */
+ if (hi_nibble > 7)
+ return 0;
+ c = (hi_nibble << 4) | lo_nibble;
+ if (c < 0x20)
+ return 0;
+ }
+ }
+
+ if (!c || len <= escape_len || e[escape_len] != '$')
+ return 0;
+
+ *out_len = 2 + escape_len;
+ return c;
}
-static int
-looks_like_rust (const char *str, size_t len)
-{
- const char *end = str + len;
-
- while (str < end)
- switch (*str)
- {
- case '$':
- if (!strncmp (str, "$C$", 3))
- str += 3;
- else if (!strncmp (str, "$SP$", 4)
- || !strncmp (str, "$BP$", 4)
- || !strncmp (str, "$RF$", 4)
- || !strncmp (str, "$LT$", 4)
- || !strncmp (str, "$GT$", 4)
- || !strncmp (str, "$LP$", 4)
- || !strncmp (str, "$RP$", 4))
- str += 4;
- else if (!strncmp (str, "$u20$", 5)
- || !strncmp (str, "$u22$", 5)
- || !strncmp (str, "$u27$", 5)
- || !strncmp (str, "$u2b$", 5)
- || !strncmp (str, "$u3b$", 5)
- || !strncmp (str, "$u5b$", 5)
- || !strncmp (str, "$u5d$", 5)
- || !strncmp (str, "$u7b$", 5)
- || !strncmp (str, "$u7d$", 5)
- || !strncmp (str, "$u7e$", 5))
- str += 5;
- else
- return 0;
- break;
- case '.':
- /* Do not allow three or more consecutive dots */
- if (!strncmp (str, "...", 3))
- return 0;
- /* Fall through */
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- case '0': case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- case '_':
- case ':':
- str++;
- break;
- default:
- return 0;
- }
-
- return 1;
-}
-
-/*
- INPUT: sym: symbol for which rust_is_mangled(sym) returned 1.
-
- The input is demangled in-place because the mangled name is always
- longer than the demangled one. */
-
-void
-rust_demangle_sym (char *sym)
-{
- const char *in;
- char *out;
- const char *end;
-
- if (!sym)
+static void
+print_ident (struct rust_demangler *rdm, struct rust_mangled_ident ident)
+{
+ char unescaped;
+ uint8_t *out, *p, d;
+ size_t len, cap, punycode_pos, j;
+ /* Punycode parameters and state. */
+ uint32_t c;
+ size_t base, t_min, t_max, skew, damp, bias, i;
+ size_t delta, w, k, t;
+
+ if (rdm->errored || rdm->skipping_printing)
return;
- in = sym;
- out = sym;
- end = sym + strlen (sym) - (hash_prefix_len + hash_len);
-
- while (in < end)
- switch (*in)
- {
- case '$':
- if (!(unescape (&in, &out, "$C$", ',')
- || unescape (&in, &out, "$SP$", '@')
- || unescape (&in, &out, "$BP$", '*')
- || unescape (&in, &out, "$RF$", '&')
- || unescape (&in, &out, "$LT$", '<')
- || unescape (&in, &out, "$GT$", '>')
- || unescape (&in, &out, "$LP$", '(')
- || unescape (&in, &out, "$RP$", ')')
- || unescape (&in, &out, "$u20$", ' ')
- || unescape (&in, &out, "$u22$", '\"')
- || unescape (&in, &out, "$u27$", '\'')
- || unescape (&in, &out, "$u2b$", '+')
- || unescape (&in, &out, "$u3b$", ';')
- || unescape (&in, &out, "$u5b$", '[')
- || unescape (&in, &out, "$u5d$", ']')
- || unescape (&in, &out, "$u7b$", '{')
- || unescape (&in, &out, "$u7d$", '}')
- || unescape (&in, &out, "$u7e$", '~'))) {
- /* unexpected escape sequence, not looks_like_rust. */
- goto fail;
+ if (rdm->version == -1)
+ {
+ /* Ignore leading underscores preceding escape sequences.
+ The mangler inserts an underscore to make sure the
+ identifier begins with a XID_Start character. */
+ if (ident.ascii_len >= 2 && ident.ascii[0] == '_'
+ && ident.ascii[1] == '$')
+ {
+ ident.ascii++;
+ ident.ascii_len--;
+ }
+
+ while (ident.ascii_len > 0)
+ {
+ /* Handle legacy escape sequences ("$...$", ".." or "."). */
+ if (ident.ascii[0] == '$')
+ {
+ unescaped
+ = decode_legacy_escape (ident.ascii, ident.ascii_len, &len);
+ if (unescaped)
+ print_str (rdm, &unescaped, 1);
+ else
+ {
+ /* Unexpected escape sequence, print the rest verbatim. */
+ print_str (rdm, ident.ascii, ident.ascii_len);
+ return;
+ }
+ }
+ else if (ident.ascii[0] == '.')
+ {
+ if (ident.ascii_len >= 2 && ident.ascii[1] == '.')
+ {
+ /* ".." becomes "::" */
+ PRINT ("::");
+ len = 2;
+ }
+ else
+ {
+ PRINT (".");
+ len = 1;
+ }
+ }
+ else
+ {
+ /* Print everything before the next escape sequence, at once. */
+ for (len = 0; len < ident.ascii_len; len++)
+ if (ident.ascii[len] == '$' || ident.ascii[len] == '.')
+ break;
+
+ print_str (rdm, ident.ascii, len);
+ }
+
+ ident.ascii += len;
+ ident.ascii_len -= len;
+ }
+
+ return;
+ }
+
+ if (!ident.punycode)
+ {
+ print_str (rdm, ident.ascii, ident.ascii_len);
+ return;
+ }
+
+ len = 0;
+ cap = 4;
+ while (cap < ident.ascii_len)
+ {
+ cap *= 2;
+ /* Check for overflows. */
+ if ((cap * 4) / 4 != cap)
+ {
+ rdm->errored = 1;
+ return;
+ }
+ }
+
+ /* Store the output codepoints as groups of 4 UTF-8 bytes. */
+ out = (uint8_t *)malloc (cap * 4);
+ if (!out)
+ {
+ rdm->errored = 1;
+ return;
+ }
+
+ /* Populate initial output from ASCII fragment. */
+ for (len = 0; len < ident.ascii_len; len++)
+ {
+ p = out + 4 * len;
+ p[0] = 0;
+ p[1] = 0;
+ p[2] = 0;
+ p[3] = ident.ascii[len];
+ }
+
+ /* Punycode parameters and initial state. */
+ base = 36;
+ t_min = 1;
+ t_max = 26;
+ skew = 38;
+ damp = 700;
+ bias = 72;
+ i = 0;
+ c = 0x80;
+
+ punycode_pos = 0;
+ while (punycode_pos < ident.punycode_len)
+ {
+ /* Read one delta value. */
+ delta = 0;
+ w = 1;
+ k = 0;
+ do
+ {
+ k += base;
+ t = k < bias ? 0 : (k - bias);
+ if (t < t_min)
+ t = t_min;
+ if (t > t_max)
+ t = t_max;
+
+ if (punycode_pos >= ident.punycode_len)
+ goto cleanup;
+ d = ident.punycode[punycode_pos++];
+
+ if (ISLOWER (d))
+ d = d - 'a';
+ else if (ISDIGIT (d))
+ d = 26 + (d - '0');
+ else
+ {
+ rdm->errored = 1;
+ goto cleanup;
+ }
+
+ delta += d * w;
+ w *= base - t;
+ }
+ while (d >= t);
+
+ /* Compute the new insert position and character. */
+ len++;
+ i += delta;
+ c += i / len;
+ i %= len;
+
+ /* Ensure enough space is available. */
+ if (cap < len)
+ {
+ cap *= 2;
+ /* Check for overflows. */
+ if ((cap * 4) / 4 != cap || cap < len)
+ {
+ rdm->errored = 1;
+ goto cleanup;
+ }
+ }
+ p = (uint8_t *)realloc (out, cap * 4);
+ if (!p)
+ {
+ rdm->errored = 1;
+ goto cleanup;
+ }
+ out = p;
+
+ /* Move the characters after the insert position. */
+ p = out + i * 4;
+ memmove (p + 4, p, (len - i - 1) * 4);
+
+ /* Insert the new character, as UTF-8 bytes. */
+ p[0] = c >= 0x10000 ? 0xf0 | (c >> 18) : 0;
+ p[1] = c >= 0x800 ? (c < 0x10000 ? 0xe0 : 0x80) | ((c >> 12) & 0x3f) : 0;
+ p[2] = (c < 0x800 ? 0xc0 : 0x80) | ((c >> 6) & 0x3f);
+ p[3] = 0x80 | (c & 0x3f);
+
+ /* If there are no more deltas, decoding is complete. */
+ if (punycode_pos == ident.punycode_len)
+ break;
+
+ i++;
+
+ /* Perform bias adaptation. */
+ delta /= damp;
+ damp = 2;
+
+ delta += delta / len;
+ k = 0;
+ while (delta > ((base - t_min) * t_max) / 2)
+ {
+ delta /= base - t_min;
+ k += base;
+ }
+ bias = k + ((base - t_min + 1) * delta) / (delta + skew);
+ }
+
+ /* Remove all the 0 bytes to leave behind an UTF-8 string. */
+ for (i = 0, j = 0; i < len * 4; i++)
+ if (out[i] != 0)
+ out[j++] = out[i];
+
+ print_str (rdm, (const char *)out, j);
+
+cleanup:
+ free (out);
+}
+
+/* Print the lifetime according to the previously decoded index.
+ An index of `0` always refers to `'_`, but starting with `1`,
+ indices refer to late-bound lifetimes introduced by a binder. */
+static void
+print_lifetime_from_index (struct rust_demangler *rdm, uint64_t lt)
+{
+ char c;
+ uint64_t depth;
+
+ PRINT ("'");
+ if (lt == 0)
+ {
+ PRINT ("_");
+ return;
+ }
+
+ depth = rdm->bound_lifetime_depth - lt;
+ /* Try to print lifetimes alphabetically first. */
+ if (depth < 26)
+ {
+ c = 'a' + depth;
+ print_str (rdm, &c, 1);
+ }
+ else
+ {
+ /* Use `'_123` after running out of letters. */
+ PRINT ("_");
+ print_uint64 (rdm, depth);
+ }
+}
+
+/* Demangling functions. */
+
+static void demangle_binder (struct rust_demangler *rdm);
+static void demangle_path (struct rust_demangler *rdm, int in_value);
+static void demangle_generic_arg (struct rust_demangler *rdm);
+static void demangle_type (struct rust_demangler *rdm);
+static int demangle_path_maybe_open_generics (struct rust_demangler *rdm);
+static void demangle_dyn_trait (struct rust_demangler *rdm);
+static void demangle_const (struct rust_demangler *rdm);
+static void demangle_const_uint (struct rust_demangler *rdm);
+static void demangle_const_int (struct rust_demangler *rdm);
+static void demangle_const_bool (struct rust_demangler *rdm);
+static void demangle_const_char (struct rust_demangler *rdm);
+
+/* Optionally enter a binder ('G') for late-bound lifetimes,
+ printing e.g. `for<'a, 'b> `, and make those lifetimes visible
+ to the caller (via depth level, which the caller should reset). */
+static void
+demangle_binder (struct rust_demangler *rdm)
+{
+ uint64_t i, bound_lifetimes;
+
+ if (rdm->errored)
+ return;
+
+ bound_lifetimes = parse_opt_integer_62 (rdm, 'G');
+ if (bound_lifetimes > 0)
+ {
+ PRINT ("for<");
+ for (i = 0; i < bound_lifetimes; i++)
+ {
+ if (i > 0)
+ PRINT (", ");
+ rdm->bound_lifetime_depth++;
+ print_lifetime_from_index (rdm, 1);
+ }
+ PRINT ("> ");
+ }
+}
+
+static void
+demangle_path (struct rust_demangler *rdm, int in_value)
+{
+ char tag, ns;
+ int was_skipping_printing;
+ size_t i, backref, old_next;
+ uint64_t dis;
+ struct rust_mangled_ident name;
+
+ if (rdm->errored)
+ return;
+
+ if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
+ {
+ ++ rdm->recursion;
+ if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
+ /* FIXME: There ought to be a way to report
+ that the recursion limit has been reached. */
+ goto fail_return;
+ }
+
+ switch (tag = next (rdm))
+ {
+ case 'C':
+ dis = parse_disambiguator (rdm);
+ name = parse_ident (rdm);
+
+ print_ident (rdm, name);
+ if (rdm->verbose)
+ {
+ PRINT ("[");
+ print_uint64_hex (rdm, dis);
+ PRINT ("]");
+ }
+ break;
+ case 'N':
+ ns = next (rdm);
+ if (!ISLOWER (ns) && !ISUPPER (ns))
+ goto fail_return;
+
+ demangle_path (rdm, in_value);
+
+ dis = parse_disambiguator (rdm);
+ name = parse_ident (rdm);
+
+ if (ISUPPER (ns))
+ {
+ /* Special namespaces, like closures and shims. */
+ PRINT ("::{");
+ switch (ns)
+ {
+ case 'C':
+ PRINT ("closure");
+ break;
+ case 'S':
+ PRINT ("shim");
+ break;
+ default:
+ print_str (rdm, &ns, 1);
+ }
+ if (name.ascii || name.punycode)
+ {
+ PRINT (":");
+ print_ident (rdm, name);
+ }
+ PRINT ("#");
+ print_uint64 (rdm, dis);
+ PRINT ("}");
+ }
+ else
+ {
+ /* Implementation-specific/unspecified namespaces. */
+
+ if (name.ascii || name.punycode)
+ {
+ PRINT ("::");
+ print_ident (rdm, name);
+ }
+ }
+ break;
+ case 'M':
+ case 'X':
+ /* Ignore the `impl`'s own path.*/
+ parse_disambiguator (rdm);
+ was_skipping_printing = rdm->skipping_printing;
+ rdm->skipping_printing = 1;
+ demangle_path (rdm, in_value);
+ rdm->skipping_printing = was_skipping_printing;
+ /* fallthrough */
+ case 'Y':
+ PRINT ("<");
+ demangle_type (rdm);
+ if (tag != 'M')
+ {
+ PRINT (" as ");
+ demangle_path (rdm, 0);
+ }
+ PRINT (">");
+ break;
+ case 'I':
+ demangle_path (rdm, in_value);
+ if (in_value)
+ PRINT ("::");
+ PRINT ("<");
+ for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
+ {
+ if (i > 0)
+ PRINT (", ");
+ demangle_generic_arg (rdm);
+ }
+ PRINT (">");
+ break;
+ case 'B':
+ backref = parse_integer_62 (rdm);
+ if (!rdm->skipping_printing)
+ {
+ old_next = rdm->next;
+ rdm->next = backref;
+ demangle_path (rdm, in_value);
+ rdm->next = old_next;
+ }
+ break;
+ default:
+ goto fail_return;
+ }
+ goto pass_return;
+
+ fail_return:
+ rdm->errored = 1;
+ pass_return:
+ if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
+ -- rdm->recursion;
+}
+
+static void
+demangle_generic_arg (struct rust_demangler *rdm)
+{
+ uint64_t lt;
+ if (eat (rdm, 'L'))
+ {
+ lt = parse_integer_62 (rdm);
+ print_lifetime_from_index (rdm, lt);
+ }
+ else if (eat (rdm, 'K'))
+ demangle_const (rdm);
+ else
+ demangle_type (rdm);
+}
+
+static const char *
+basic_type (char tag)
+{
+ switch (tag)
+ {
+ case 'b':
+ return "bool";
+ case 'c':
+ return "char";
+ case 'e':
+ return "str";
+ case 'u':
+ return "()";
+ case 'a':
+ return "i8";
+ case 's':
+ return "i16";
+ case 'l':
+ return "i32";
+ case 'x':
+ return "i64";
+ case 'n':
+ return "i128";
+ case 'i':
+ return "isize";
+ case 'h':
+ return "u8";
+ case 't':
+ return "u16";
+ case 'm':
+ return "u32";
+ case 'y':
+ return "u64";
+ case 'o':
+ return "u128";
+ case 'j':
+ return "usize";
+ case 'f':
+ return "f32";
+ case 'd':
+ return "f64";
+ case 'z':
+ return "!";
+ case 'p':
+ return "_";
+ case 'v':
+ return "...";
+
+ default:
+ return NULL;
+ }
+}
+
+static void
+demangle_type (struct rust_demangler *rdm)
+{
+ char tag;
+ size_t i, old_next, backref;
+ uint64_t lt, old_bound_lifetime_depth;
+ const char *basic;
+ struct rust_mangled_ident abi;
+
+ if (rdm->errored)
+ return;
+
+ tag = next (rdm);
+
+ basic = basic_type (tag);
+ if (basic)
+ {
+ PRINT (basic);
+ return;
+ }
+
+ if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
+ {
+ ++ rdm->recursion;
+ if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
+ /* FIXME: There ought to be a way to report
+ that the recursion limit has been reached. */
+ {
+ rdm->errored = 1;
+ -- rdm->recursion;
+ return;
+ }
+ }
+
+ switch (tag)
+ {
+ case 'R':
+ case 'Q':
+ PRINT ("&");
+ if (eat (rdm, 'L'))
+ {
+ lt = parse_integer_62 (rdm);
+ if (lt)
+ {
+ print_lifetime_from_index (rdm, lt);
+ PRINT (" ");
+ }
+ }
+ if (tag != 'R')
+ PRINT ("mut ");
+ demangle_type (rdm);
+ break;
+ case 'P':
+ case 'O':
+ PRINT ("*");
+ if (tag != 'P')
+ PRINT ("mut ");
+ else
+ PRINT ("const ");
+ demangle_type (rdm);
+ break;
+ case 'A':
+ case 'S':
+ PRINT ("[");
+ demangle_type (rdm);
+ if (tag == 'A')
+ {
+ PRINT ("; ");
+ demangle_const (rdm);
+ }
+ PRINT ("]");
+ break;
+ case 'T':
+ PRINT ("(");
+ for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
+ {
+ if (i > 0)
+ PRINT (", ");
+ demangle_type (rdm);
+ }
+ if (i == 1)
+ PRINT (",");
+ PRINT (")");
+ break;
+ case 'F':
+ old_bound_lifetime_depth = rdm->bound_lifetime_depth;
+ demangle_binder (rdm);
+
+ if (eat (rdm, 'U'))
+ PRINT ("unsafe ");
+
+ if (eat (rdm, 'K'))
+ {
+ if (eat (rdm, 'C'))
+ {
+ abi.ascii = "C";
+ abi.ascii_len = 1;
+ }
+ else
+ {
+ abi = parse_ident (rdm);
+ if (!abi.ascii || abi.punycode)
+ {
+ rdm->errored = 1;
+ goto restore;
+ }
+ }
+
+ PRINT ("extern \"");
+
+ /* If the ABI had any `-`, they were replaced with `_`,
+ so the parts between `_` have to be re-joined with `-`. */
+ for (i = 0; i < abi.ascii_len; i++)
+ {
+ if (abi.ascii[i] == '_')
+ {
+ print_str (rdm, abi.ascii, i);
+ PRINT ("-");
+ abi.ascii += i + 1;
+ abi.ascii_len -= i + 1;
+ i = 0;
+ }
+ }
+ print_str (rdm, abi.ascii, abi.ascii_len);
+
+ PRINT ("\" ");
+ }
+
+ PRINT ("fn(");
+ for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
+ {
+ if (i > 0)
+ PRINT (", ");
+ demangle_type (rdm);
+ }
+ PRINT (")");
+
+ if (eat (rdm, 'u'))
+ {
+ /* Skip printing the return type if it's 'u', i.e. `()`. */
+ }
+ else
+ {
+ PRINT (" -> ");
+ demangle_type (rdm);
+ }
+
+ /* Restore `bound_lifetime_depth` to outside the binder. */
+ restore:
+ rdm->bound_lifetime_depth = old_bound_lifetime_depth;
+ break;
+ case 'D':
+ PRINT ("dyn ");
+
+ old_bound_lifetime_depth = rdm->bound_lifetime_depth;
+ demangle_binder (rdm);
+
+ for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
+ {
+ if (i > 0)
+ PRINT (" + ");
+ demangle_dyn_trait (rdm);
+ }
+
+ /* Restore `bound_lifetime_depth` to outside the binder. */
+ rdm->bound_lifetime_depth = old_bound_lifetime_depth;
+
+ if (!eat (rdm, 'L'))
+ {
+ rdm->errored = 1;
+ return;
+ }
+ lt = parse_integer_62 (rdm);
+ if (lt)
+ {
+ PRINT (" + ");
+ print_lifetime_from_index (rdm, lt);
+ }
+ break;
+ case 'B':
+ backref = parse_integer_62 (rdm);
+ if (!rdm->skipping_printing)
+ {
+ old_next = rdm->next;
+ rdm->next = backref;
+ demangle_type (rdm);
+ rdm->next = old_next;
+ }
+ break;
+ default:
+ /* Go back to the tag, so `demangle_path` also sees it. */
+ rdm->next--;
+ demangle_path (rdm, 0);
+ }
+
+ if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
+ -- rdm->recursion;
+}
+
+/* A trait in a trait object may have some "existential projections"
+ (i.e. associated type bindings) after it, which should be printed
+ in the `<...>` of the trait, e.g. `dyn Trait<T, U, Assoc=X>`.
+ To this end, this method will keep the `<...>` of an 'I' path
+ open, by omitting the `>`, and return `Ok(true)` in that case. */
+static int
+demangle_path_maybe_open_generics (struct rust_demangler *rdm)
+{
+ int open;
+ size_t i, old_next, backref;
+
+ open = 0;
+
+ if (rdm->errored)
+ return open;
+
+ if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
+ {
+ ++ rdm->recursion;
+ if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
+ {
+ /* FIXME: There ought to be a way to report
+ that the recursion limit has been reached. */
+ rdm->errored = 1;
+ goto end_of_func;
}
- break;
- case '_':
- /* If this is the start of a path component and the next
- character is an escape sequence, ignore the underscore. The
- mangler inserts an underscore to make sure the path
- component begins with a XID_Start character. */
- if ((in == sym || in[-1] == ':') && in[1] == '$')
- in++;
- else
- *out++ = *in++;
- break;
- case '.':
- if (in[1] == '.')
- {
- /* ".." becomes "::" */
- *out++ = ':';
- *out++ = ':';
- in += 2;
- }
- else
- {
- /* "." becomes "-" */
- *out++ = '-';
- in++;
- }
- break;
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
- case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
- case 's': case 't': case 'u': case 'v': case 'w': case 'x':
- case 'y': case 'z':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
- case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
- case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
- case 'Y': case 'Z':
- case '0': case '1': case '2': case '3': case '4': case '5':
- case '6': case '7': case '8': case '9':
- case ':':
- *out++ = *in++;
- break;
- default:
- /* unexpected character in symbol, not looks_like_rust. */
- goto fail;
- }
- goto done;
-
-fail:
- *out++ = '?'; /* This is pretty lame, but it's hard to do better. */
-done:
- *out = '\0';
+ }
+
+ if (eat (rdm, 'B'))
+ {
+ backref = parse_integer_62 (rdm);
+ if (!rdm->skipping_printing)
+ {
+ old_next = rdm->next;
+ rdm->next = backref;
+ open = demangle_path_maybe_open_generics (rdm);
+ rdm->next = old_next;
+ }
+ }
+ else if (eat (rdm, 'I'))
+ {
+ demangle_path (rdm, 0);
+ PRINT ("<");
+ open = 1;
+ for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
+ {
+ if (i > 0)
+ PRINT (", ");
+ demangle_generic_arg (rdm);
+ }
+ }
+ else
+ demangle_path (rdm, 0);
+
+ end_of_func:
+ if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
+ -- rdm->recursion;
+
+ return open;
+}
+
+static void
+demangle_dyn_trait (struct rust_demangler *rdm)
+{
+ int open;
+ struct rust_mangled_ident name;
+
+ if (rdm->errored)
+ return;
+
+ open = demangle_path_maybe_open_generics (rdm);
+
+ while (eat (rdm, 'p'))
+ {
+ if (!open)
+ PRINT ("<");
+ else
+ PRINT (", ");
+ open = 1;
+
+ name = parse_ident (rdm);
+ print_ident (rdm, name);
+ PRINT (" = ");
+ demangle_type (rdm);
+ }
+
+ if (open)
+ PRINT (">");
}
+static void
+demangle_const (struct rust_demangler *rdm)
+{
+ char ty_tag;
+ size_t old_next, backref;
+
+ if (rdm->errored)
+ return;
+
+ if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
+ {
+ ++ rdm->recursion;
+ if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
+ /* FIXME: There ought to be a way to report
+ that the recursion limit has been reached. */
+ goto fail_return;
+ }
+
+ if (eat (rdm, 'B'))
+ {
+ backref = parse_integer_62 (rdm);
+ if (!rdm->skipping_printing)
+ {
+ old_next = rdm->next;
+ rdm->next = backref;
+ demangle_const (rdm);
+ rdm->next = old_next;
+ }
+ goto pass_return;
+ }
+
+ ty_tag = next (rdm);
+ switch (ty_tag)
+ {
+ /* Placeholder. */
+ case 'p':
+ PRINT ("_");
+ goto pass_return;
+
+ /* Unsigned integer types. */
+ case 'h':
+ case 't':
+ case 'm':
+ case 'y':
+ case 'o':
+ case 'j':
+ demangle_const_uint (rdm);
+ break;
+
+ /* Signed integer types. */
+ case 'a':
+ case 's':
+ case 'l':
+ case 'x':
+ case 'n':
+ case 'i':
+ demangle_const_int (rdm);
+ break;
+
+ /* Boolean. */
+ case 'b':
+ demangle_const_bool (rdm);
+ break;
+
+ /* Character. */
+ case 'c':
+ demangle_const_char (rdm);
+ break;
+
+ default:
+ goto fail_return;
+ }
+
+ if (!rdm->errored && rdm->verbose)
+ {
+ PRINT (": ");
+ PRINT (basic_type (ty_tag));
+ }
+ goto pass_return;
+
+ fail_return:
+ rdm->errored = 1;
+ pass_return:
+ if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
+ -- rdm->recursion;
+}
+
+static void
+demangle_const_uint (struct rust_demangler *rdm)
+{
+ size_t hex_len;
+ uint64_t value;
+
+ if (rdm->errored)
+ return;
+
+ hex_len = parse_hex_nibbles (rdm, &value);
+
+ if (hex_len > 16)
+ {
+ /* Print anything that doesn't fit in `uint64_t` verbatim. */
+ PRINT ("0x");
+ print_str (rdm, rdm->sym + (rdm->next - hex_len), hex_len);
+ }
+ else if (hex_len > 0)
+ print_uint64 (rdm, value);
+ else
+ rdm->errored = 1;
+}
+
+static void
+demangle_const_int (struct rust_demangler *rdm)
+{
+ if (eat (rdm, 'n'))
+ PRINT ("-");
+ demangle_const_uint (rdm);
+}
+
+static void
+demangle_const_bool (struct rust_demangler *rdm)
+{
+ uint64_t value;
+
+ if (parse_hex_nibbles (rdm, &value) != 1)
+ {
+ rdm->errored = 1;
+ return;
+ }
+
+ if (value == 0)
+ PRINT ("false");
+ else if (value == 1)
+ PRINT ("true");
+ else
+ rdm->errored = 1;
+}
+
+static void
+demangle_const_char (struct rust_demangler *rdm)
+{
+ size_t hex_len;
+ uint64_t value;
+
+ hex_len = parse_hex_nibbles (rdm, &value);
+
+ if (hex_len == 0 || hex_len > 8)
+ {
+ rdm->errored = 1;
+ return;
+ }
+
+ /* Match Rust's character "debug" output as best as we can. */
+ PRINT ("'");
+ if (value == '\t')
+ PRINT ("\\t");
+ else if (value == '\r')
+ PRINT ("\\r");
+ else if (value == '\n')
+ PRINT ("\\n");
+ else if (value > ' ' && value < '~')
+ {
+ /* Rust also considers many non-ASCII codepoints to be printable, but
+ that logic is not easily ported to C. */
+ char c = value;
+ print_str (rdm, &c, 1);
+ }
+ else
+ {
+ PRINT ("\\u{");
+ print_uint64_hex (rdm, value);
+ PRINT ("}");
+ }
+ PRINT ("'");
+}
+
+/* A legacy hash is the prefix "h" followed by 16 lowercase hex digits.
+ The hex digits must contain at least 5 distinct digits. */
static int
-unescape (const char **in, char **out, const char *seq, char value)
+is_legacy_prefixed_hash (struct rust_mangled_ident ident)
{
- size_t len = strlen (seq);
+ uint16_t seen;
+ int nibble;
+ size_t i, count;
+
+ if (ident.ascii_len != 17 || ident.ascii[0] != 'h')
+ return 0;
+
+ seen = 0;
+ for (i = 0; i < 16; i++)
+ {
+ nibble = decode_lower_hex_nibble (ident.ascii[1 + i]);
+ if (nibble < 0)
+ return 0;
+ seen |= (uint16_t)1 << nibble;
+ }
+
+ /* Count how many distinct digits were seen. */
+ count = 0;
+ while (seen)
+ {
+ if (seen & 1)
+ count++;
+ seen >>= 1;
+ }
+
+ return count >= 5;
+}
+
+int
+rust_demangle_callback (const char *mangled, int options,
+ demangle_callbackref callback, void *opaque)
+{
+ const char *p;
+ struct rust_demangler rdm;
+ struct rust_mangled_ident ident;
+
+ rdm.sym = mangled;
+ rdm.sym_len = 0;
+
+ rdm.callback_opaque = opaque;
+ rdm.callback = callback;
+
+ rdm.next = 0;
+ rdm.errored = 0;
+ rdm.skipping_printing = 0;
+ rdm.verbose = (options & DMGL_VERBOSE) != 0;
+ rdm.version = 0;
+ rdm.recursion = (options & DMGL_NO_RECURSE_LIMIT) ? RUST_NO_RECURSION_LIMIT : 0;
+ rdm.bound_lifetime_depth = 0;
- if (strncmp (*in, seq, len))
+ /* Rust symbols always start with _R (v0) or _ZN (legacy). */
+ if (rdm.sym[0] == '_' && rdm.sym[1] == 'R')
+ rdm.sym += 2;
+ else if (rdm.sym[0] == '_' && rdm.sym[1] == 'Z' && rdm.sym[2] == 'N')
+ {
+ rdm.sym += 3;
+ rdm.version = -1;
+ }
+ else
return 0;
- **out = value;
+ /* Paths (v0) always start with uppercase characters. */
+ if (rdm.version != -1 && !ISUPPER (rdm.sym[0]))
+ return 0;
+
+ /* Rust symbols (v0) use only [_0-9a-zA-Z] characters. */
+ for (p = rdm.sym; *p; p++)
+ {
+ /* Rust v0 symbols can have '.' suffixes, ignore those. */
+ if (rdm.version == 0 && *p == '.')
+ break;
+
+ rdm.sym_len++;
+
+ if (*p == '_' || ISALNUM (*p))
+ continue;
+
+ /* Legacy Rust symbols can also contain [.:$] characters.
+ Or @ in the .suffix (which will be skipped, see below). */
+ if (rdm.version == -1 && (*p == '$' || *p == '.' || *p == ':'
+ || *p == '@'))
+ continue;
+
+ return 0;
+ }
+
+ /* Legacy Rust symbols need to be handled separately. */
+ if (rdm.version == -1)
+ {
+ /* Legacy Rust symbols always end with E. But can be followed by a
+ .suffix (which we want to ignore). */
+ int dot_suffix = 1;
+ while (rdm.sym_len > 0 &&
+ !(dot_suffix && rdm.sym[rdm.sym_len - 1] == 'E'))
+ {
+ dot_suffix = rdm.sym[rdm.sym_len - 1] == '.';
+ rdm.sym_len--;
+ }
+
+ if (!(rdm.sym_len > 0 && rdm.sym[rdm.sym_len - 1] == 'E'))
+ return 0;
+ rdm.sym_len--;
+
+ /* Legacy Rust symbols also always end with a path segment
+ that encodes a 16 hex digit hash, i.e. '17h[a-f0-9]{16}'.
+ This early check, before any parse_ident calls, should
+ quickly filter out most C++ symbols unrelated to Rust. */
+ if (!(rdm.sym_len > 19
+ && !memcmp (&rdm.sym[rdm.sym_len - 19], "17h", 3)))
+ return 0;
+
+ do
+ {
+ ident = parse_ident (&rdm);
+ if (rdm.errored || !ident.ascii)
+ return 0;
+ }
+ while (rdm.next < rdm.sym_len);
+
+ /* The last path segment should be the hash. */
+ if (!is_legacy_prefixed_hash (ident))
+ return 0;
+
+ /* Reset the state for a second pass, to print the symbol. */
+ rdm.next = 0;
+ if (!rdm.verbose && rdm.sym_len > 19)
+ {
+ /* Hide the last segment, containing the hash, if not verbose. */
+ rdm.sym_len -= 19;
+ }
+
+ do
+ {
+ if (rdm.next > 0)
+ print_str (&rdm, "::", 2);
+
+ ident = parse_ident (&rdm);
+ print_ident (&rdm, ident);
+ }
+ while (rdm.next < rdm.sym_len);
+ }
+ else
+ {
+ demangle_path (&rdm, 1);
+
+ /* Skip instantiating crate. */
+ if (!rdm.errored && rdm.next < rdm.sym_len)
+ {
+ rdm.skipping_printing = 1;
+ demangle_path (&rdm, 0);
+ }
+
+ /* It's an error to not reach the end. */
+ rdm.errored |= rdm.next != rdm.sym_len;
+ }
+
+ return !rdm.errored;
+}
+
+/* Growable string buffers. */
+struct str_buf
+{
+ char *ptr;
+ size_t len;
+ size_t cap;
+ int errored;
+};
+
+static void
+str_buf_reserve (struct str_buf *buf, size_t extra)
+{
+ size_t available, min_new_cap, new_cap;
+ char *new_ptr;
+
+ /* Allocation failed before. */
+ if (buf->errored)
+ return;
+
+ available = buf->cap - buf->len;
+
+ if (extra <= available)
+ return;
+
+ min_new_cap = buf->cap + (extra - available);
+
+ /* Check for overflows. */
+ if (min_new_cap < buf->cap)
+ {
+ buf->errored = 1;
+ return;
+ }
+
+ new_cap = buf->cap;
+
+ if (new_cap == 0)
+ new_cap = 4;
+
+ /* Double capacity until sufficiently large. */
+ while (new_cap < min_new_cap)
+ {
+ new_cap *= 2;
+
+ /* Check for overflows. */
+ if (new_cap < buf->cap)
+ {
+ buf->errored = 1;
+ return;
+ }
+ }
+
+ new_ptr = (char *)realloc (buf->ptr, new_cap);
+ if (new_ptr == NULL)
+ {
+ free (buf->ptr);
+ buf->ptr = NULL;
+ buf->len = 0;
+ buf->cap = 0;
+ buf->errored = 1;
+ }
+ else
+ {
+ buf->ptr = new_ptr;
+ buf->cap = new_cap;
+ }
+}
+
+static void
+str_buf_append (struct str_buf *buf, const char *data, size_t len)
+{
+ str_buf_reserve (buf, len);
+ if (buf->errored)
+ return;
+
+ memcpy (buf->ptr + buf->len, data, len);
+ buf->len += len;
+}
+
+static void
+str_buf_demangle_callback (const char *data, size_t len, void *opaque)
+{
+ str_buf_append ((struct str_buf *)opaque, data, len);
+}
+
+char *
+rust_demangle (const char *mangled, int options)
+{
+ struct str_buf out;
+ int success;
+
+ out.ptr = NULL;
+ out.len = 0;
+ out.cap = 0;
+ out.errored = 0;
+
+ success = rust_demangle_callback (mangled, options,
+ str_buf_demangle_callback, &out);
- *in += len;
- *out += 1;
+ if (!success)
+ {
+ free (out.ptr);
+ return NULL;
+ }
- return 1;
+ str_buf_append (&out, "\0", 1);
+ return out.ptr;
}
diff --git a/rtemstoolkit/libiberty/safe-ctype.c b/rtemstoolkit/libiberty/safe-ctype.c
index 5a13f82..483b41e 100644
--- a/rtemstoolkit/libiberty/safe-ctype.c
+++ b/rtemstoolkit/libiberty/safe-ctype.c
@@ -1,6 +1,6 @@
/* <ctype.h> replacement macros.
- Copyright (C) 2000-2017 Free Software Foundation, Inc.
+ Copyright (C) 2000-2023 Free Software Foundation, Inc.
Contributed by Zack Weinberg <zackw@stanford.edu>.
This file is part of the libiberty library.
diff --git a/rtemstoolkit/libiberty/stpcpy.c b/rtemstoolkit/libiberty/stpcpy.c
index ddbef28..c996bce 100644
--- a/rtemstoolkit/libiberty/stpcpy.c
+++ b/rtemstoolkit/libiberty/stpcpy.c
@@ -1,5 +1,5 @@
/* Implement the stpcpy function.
- Copyright (C) 2003-2017 Free Software Foundation, Inc.
+ Copyright (C) 2003-2023 Free Software Foundation, Inc.
Written by Kaveh R. Ghazi <ghazi@caip.rutgers.edu>.
This file is part of the libiberty library.
@@ -33,7 +33,7 @@ Copies the string @var{src} into @var{dst}. Returns a pointer to
#include <stddef.h>
extern size_t strlen (const char *);
-extern PTR memcpy (PTR, const PTR, size_t);
+extern void *memcpy (void *, const void *, size_t);
char *
stpcpy (char *dst, const char *src)
diff --git a/rtemstoolkit/libiberty/xexit.c b/rtemstoolkit/libiberty/xexit.c
new file mode 100644
index 0000000..03b69fd
--- /dev/null
+++ b/rtemstoolkit/libiberty/xexit.c
@@ -0,0 +1,52 @@
+/* xexit.c -- Run any exit handlers, then exit.
+ Copyright (C) 1994-2023 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If not, write
+to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+Boston, MA 02110-1301, USA. */
+
+/*
+
+@deftypefn Replacement void xexit (int @var{code})
+
+Terminates the program. If any functions have been registered with
+the @code{xatexit} replacement function, they will be called first.
+Termination is handled via the system's normal @code{exit} call.
+
+@end deftypefn
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include "libiberty.h"
+
+
+/* This variable is set by xatexit if it is called. This way, xmalloc
+ doesn't drag xatexit into the link. */
+void (*_xexit_cleanup) (void);
+
+void
+xexit (int code)
+{
+ if (_xexit_cleanup != NULL)
+ (*_xexit_cleanup) ();
+ exit (code);
+}
diff --git a/rtemstoolkit/libiberty/xmalloc.c b/rtemstoolkit/libiberty/xmalloc.c
index 6d0aca4..c30b896 100644
--- a/rtemstoolkit/libiberty/xmalloc.c
+++ b/rtemstoolkit/libiberty/xmalloc.c
@@ -1,6 +1,6 @@
/* memory allocation routines with error checking.
- Copyright (C) 1989-2017 Free Software Foundation, Inc.
-
+ Copyright (C) 1989-2023 Free Software Foundation, Inc.
+
This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -68,7 +68,6 @@ function will be called to print an error message and terminate execution.
#include "environ.h"
#include <stdio.h>
-#include <stdlib.h>
#include <stddef.h>
@@ -88,7 +87,9 @@ extern "C" {
void *malloc (size_t);
void *realloc (void *, size_t);
void *calloc (size_t, size_t);
+#ifdef HAVE_SBRK
void *sbrk (ptrdiff_t);
+#endif
# ifdef __cplusplus
}
# endif /* __cplusplus */
@@ -135,13 +136,13 @@ xmalloc_failed (size_t size)
name, *name ? ": " : "",
(unsigned long) size);
#endif /* HAVE_SBRK */
- exit (1);
-}
+ xexit (1);
+}
-PTR
+void *
xmalloc (size_t size)
{
- PTR newmem;
+ void *newmem;
if (size == 0)
size = 1;
@@ -152,10 +153,10 @@ xmalloc (size_t size)
return (newmem);
}
-PTR
+void *
xcalloc (size_t nelem, size_t elsize)
{
- PTR newmem;
+ void *newmem;
if (nelem == 0 || elsize == 0)
nelem = elsize = 1;
@@ -167,10 +168,10 @@ xcalloc (size_t nelem, size_t elsize)
return (newmem);
}
-PTR
-xrealloc (PTR oldmem, size_t size)
+void *
+xrealloc (void *oldmem, size_t size)
{
- PTR newmem;
+ void *newmem;
if (size == 0)
size = 1;
diff --git a/rtemstoolkit/libiberty/xmemdup.c b/rtemstoolkit/libiberty/xmemdup.c
index 4602afd..f2ed41f 100644
--- a/rtemstoolkit/libiberty/xmemdup.c
+++ b/rtemstoolkit/libiberty/xmemdup.c
@@ -31,11 +31,11 @@ allocated, the remaining memory is zeroed.
# endif
#endif
-PTR
-xmemdup (const PTR input, size_t copy_size, size_t alloc_size)
+void *
+xmemdup (const void *input, size_t copy_size, size_t alloc_size)
{
- PTR output = xmalloc (alloc_size);
+ void *output = xmalloc (alloc_size);
if (alloc_size > copy_size)
memset ((char *) output + copy_size, 0, alloc_size - copy_size);
- return (PTR) memcpy (output, input, copy_size);
+ return (void *) memcpy (output, input, copy_size);
}
diff --git a/rtemstoolkit/macros.py b/rtemstoolkit/macros.py
index d8012e1..136f52d 100644
--- a/rtemstoolkit/macros.py
+++ b/rtemstoolkit/macros.py
@@ -260,7 +260,7 @@ class macros:
return [rm[7:] for rm in self.read_maps]
def key_filter(self, key):
- if key.startswith('%{') and key[-1] is '}':
+ if key.startswith('%{') and key[-1] == '}':
key = key[2:-1]
return key.lower()
@@ -295,29 +295,29 @@ class macros:
print(']]]]]]]] c:%s(%d) s:%s t:"%s" m:%r M:%s' % \
(c, ord(c), state, token, macro, map))
l_remaining = l_remaining[1:]
- if c is '#' and not state.startswith('value'):
+ if c == '#' and not state.startswith('value'):
break
if c == '\n' or c == '\r':
- if not (state is 'key' and len(token) == 0) and \
+ if not (state == 'key' and len(token) == 0) and \
not state.startswith('value-multiline'):
self.macros = orig_macros
raise error.general('malformed macro line:%d: %s' % (lc, l))
- if state is 'key':
+ if state == 'key':
if c not in string.whitespace:
- if c is '[':
+ if c == '[':
state = 'map'
- elif c is '%':
+ elif c == '%':
state = 'directive'
- elif c is ':':
+ elif c == ':':
macro += [token]
token = ''
state = 'attribs'
- elif c is '#':
+ elif c == '#':
break
else:
token += c
- elif state is 'map':
- if c is ']':
+ elif state == 'map':
+ if c == ']':
if token not in self.macros:
self.macros[token] = {}
map = token
@@ -328,7 +328,7 @@ class macros:
else:
self.macros = orig_macros
raise error.general('invalid macro map:%d: %s' % (lc, l))
- elif state is 'directive':
+ elif state == 'directive':
if c in string.whitespace:
if token == 'include':
self.load(_clean(l_remaining))
@@ -340,7 +340,7 @@ class macros:
else:
self.macros = orig_macros
raise error.general('invalid macro directive:%d: %s' % (lc, l))
- elif state is 'include':
+ elif state == 'include':
if c is string.whitespace:
if token == 'include':
state = 'include'
@@ -349,49 +349,49 @@ class macros:
else:
self.macros = orig_macros
raise error.general('invalid macro directive:%d: %s' % (lc, l))
- elif state is 'attribs':
+ elif state == 'attribs':
if c not in string.whitespace:
- if c is ',':
+ if c == ',':
macro += [token]
token = ''
if len(macro) == 3:
state = 'value-start'
else:
token += c
- elif state is 'value-start':
- if c is "'":
+ elif state == 'value-start':
+ if c == "'":
state = 'value-line-start'
- elif state is 'value-line-start':
- if c is "'":
+ elif state == 'value-line-start':
+ if c == "'":
state = 'value-multiline-start'
else:
state = 'value-line'
token += c
- elif state is 'value-multiline-start':
- if c is "'":
+ elif state == 'value-multiline-start':
+ if c == "'":
state = 'value-multiline'
else:
macro += [token]
state = 'macro'
- elif state is 'value-line':
- if c is "'":
+ elif state == 'value-line':
+ if c == "'":
macro += [token]
state = 'macro'
else:
token += c
- elif state is 'value-multiline':
- if c is "'":
+ elif state == 'value-multiline':
+ if c == "'":
state = 'value-multiline-end'
else:
token += c
- elif state is 'value-multiline-end':
- if c is "'":
+ elif state == 'value-multiline-end':
+ if c == "'":
state = 'value-multiline-end-end'
else:
state = 'value-multiline'
token += "'" + c
- elif state is 'value-multiline-end-end':
- if c is "'":
+ elif state == 'value-multiline-end-end':
+ if c == "'":
macro += [token]
state = 'macro'
else:
@@ -400,7 +400,7 @@ class macros:
else:
self.macros = orig_macros
raise error.internal('bad state: %s' % (state))
- if state is 'macro':
+ if state == 'macro':
self.macros[map][macro[0].lower()] = (macro[1], macro[2], macro[3])
macro = []
token = ''
diff --git a/rtemstoolkit/rld-dwarf.cpp b/rtemstoolkit/rld-dwarf.cpp
index e41be8c..c11a13e 100644
--- a/rtemstoolkit/rld-dwarf.cpp
+++ b/rtemstoolkit/rld-dwarf.cpp
@@ -1812,7 +1812,7 @@ namespace rld
debug_info_entry next (die.get_debug ());
if (die.get_child (next))
- load_functions (next);
+ load_variables (next);
if (!die.get_sibling (next))
break;
diff --git a/rtemstoolkit/rld-elf.cpp b/rtemstoolkit/rld-elf.cpp
index ffa3376..b131461 100644
--- a/rtemstoolkit/rld-elf.cpp
+++ b/rtemstoolkit/rld-elf.cpp
@@ -200,18 +200,6 @@ namespace rld
<< std::endl;
}
- section::section (const section& orig)
- : file_ (orig.file_),
- index_ (orig.index_),
- name_ (orig.name_),
- scn (orig.scn),
- shdr (orig.shdr),
- data_ (orig.data_),
- rela (orig.rela),
- relocs (orig.relocs)
- {
- }
-
section::section ()
: file_ (0),
index_ (-1),
@@ -891,6 +879,7 @@ namespace rld
{
if (((stype == STT_NOTYPE) ||
(stype == STT_OBJECT) ||
+ (stype == STT_TLS) ||
(stype == STT_FUNC)) &&
((weak && (sbind == STB_WEAK)) ||
(!unresolved && ((local && (sbind == STB_LOCAL)) ||
@@ -1189,6 +1178,7 @@ namespace rld
{ "m68k", EM_COLDFIRE },
{ "mips", EM_MIPS },
{ "powerpc", EM_PPC },
+ { "powerpc", EM_PPC64 },
#ifndef EM_RISCV
{ "riscv", 243 }, /* If not in libelf yet */
#else
diff --git a/rtemstoolkit/rld-elf.h b/rtemstoolkit/rld-elf.h
index 4919135..d66eee5 100644
--- a/rtemstoolkit/rld-elf.h
+++ b/rtemstoolkit/rld-elf.h
@@ -175,11 +175,6 @@ namespace rld
section (file& file_, int index);
/**
- * Copy constructor.
- */
- section (const section& orig);
-
- /**
* Default constructor.
*/
section ();
diff --git a/rtemstoolkit/rld-path.cpp b/rtemstoolkit/rld-path.cpp
index 35af055..61c954d 100644
--- a/rtemstoolkit/rld-path.cpp
+++ b/rtemstoolkit/rld-path.cpp
@@ -109,23 +109,13 @@ namespace rld
}
else
{
- char* buf = 0;
- try
+ std::vector<char> buf (32 * 1024);
+ if (!::getcwd (buf.data(), buf.size()))
{
- buf = new char[32 * 1024];
- if (!::getcwd (buf, 32 * 1024))
- {
- delete [] buf;
- throw rld::error (::strerror (errno), "get current working directory");
- }
- path_join (buf, path, apath);
- delete [] buf;
- }
- catch (...)
- {
- delete [] buf;
- throw;
+ throw rld::error (::strerror (errno), "get current working directory");
}
+ std::string cwd (buf.data());
+ path_join (cwd, path, apath);
}
strings ps;
diff --git a/rtemstoolkit/rld-rap.cpp b/rtemstoolkit/rld-rap.cpp
index 93ffbdc..045536a 100644
--- a/rtemstoolkit/rld-rap.cpp
+++ b/rtemstoolkit/rld-rap.cpp
@@ -696,8 +696,7 @@ namespace rld
* Helper for for_each to merge the related object sections into the RAP
* section.
*/
- class section_merge:
- public std::unary_function < const files::section, void >
+ class section_merge
{
public:
@@ -1191,8 +1190,7 @@ namespace rld
/**
* Helper for for_each to write out the various sections.
*/
- class section_writer:
- public std::unary_function < object, void >
+ class section_writer
{
public:
@@ -1364,8 +1362,6 @@ namespace rld
void
image::write_relocations (compress::compressor& comp)
{
- uint32_t rr = 0;
-
for (int s = 0; s < rap_secs; ++s)
{
uint32_t count = get_relocations (s);
@@ -1515,7 +1511,6 @@ namespace rld
++rc;
++sr;
- ++rr;
}
}
}
diff --git a/rtemstoolkit/rld-symbols.cpp b/rtemstoolkit/rld-symbols.cpp
index 661b598..01291c0 100644
--- a/rtemstoolkit/rld-symbols.cpp
+++ b/rtemstoolkit/rld-symbols.cpp
@@ -277,6 +277,9 @@ namespace rld
case STT_FILE:
type = "STT_FILE ";
break;
+ case STT_TLS:
+ type = "STT_TLS ";
+ break;
default:
if ((type_val >= STT_LOPROC) && (type_val <= STT_HIPROC))
type = "STT_LOPROC(" + rld::to_string (type_val) + ")";
diff --git a/rtemstoolkit/rld.cpp b/rtemstoolkit/rld.cpp
index 90fb39d..d0848ad 100644
--- a/rtemstoolkit/rld.cpp
+++ b/rtemstoolkit/rld.cpp
@@ -106,9 +106,10 @@ namespace rld
ltrim (const std::string& s)
{
std::string t = s;
- t.erase (t.begin (),
- std::find_if (t.begin (), t.end (),
- std::not1 (std::ptr_fun < int, int > (std::isspace))));
+ auto non_space =
+ std::find_if (t.begin (), t.end (),
+ [](unsigned char c) { return !std::isspace (c); });
+ t.erase (t.begin (), non_space);
return t;
}
@@ -116,9 +117,10 @@ namespace rld
rtrim (const std::string& s)
{
std::string t = s;
- t.erase (std::find_if (t.rbegin (), t.rend (),
- std::not1 (std::ptr_fun < int, int > (std::isspace))).base(),
- t.end());
+ auto last_space =
+ std::find_if (t.rbegin (), t.rend (),
+ [](unsigned char c) { return !std::isspace (c); }).base();
+ t.erase (last_space, t.end());
return t;
}
diff --git a/rtemstoolkit/version.py b/rtemstoolkit/version.py
index 56cf97f..51188bf 100644
--- a/rtemstoolkit/version.py
+++ b/rtemstoolkit/version.py
@@ -116,7 +116,7 @@ def _load_released_version_config():
os.path.join('..', 'VERSION'),
rtems.configuration_file('rtems-version.ini')]:
if path.exists(path.join(ver)):
- v = configparser.SafeConfigParser()
+ v = configparser.ConfigParser()
try:
v.read(path.host(ver))
except Exception as e:
diff --git a/rtemstoolkit/wscript b/rtemstoolkit/wscript
index bd7254b..0611e2e 100644
--- a/rtemstoolkit/wscript
+++ b/rtemstoolkit/wscript
@@ -168,7 +168,7 @@ def rebuild(ctx):
waflib.Options.commands.extend(['clean', 'build'])
def tags(ctx):
- ctx.exec_command('etags $(find . -name \*.[sSch])', shell = True)
+ ctx.exec_command('etags $(find . -name \\*.[sSch])', shell = True)
#
# Libelf module.
@@ -397,16 +397,19 @@ def conf_libiberty(conf):
conf.write_config_header('libiberty/config.h')
def bld_libiberty(bld, conf):
+ defines = ['HAVE_CONFIG_H=1']
if bld.env.DEST_OS == 'win32':
- pex_host = 'libiberty/pex-win32.c'
+ pex_host = ['libiberty/pex-win32.c','libiberty/argv.c']
else:
- pex_host = 'libiberty/pex-unix.c'
+ pex_host = ['libiberty/pex-unix.c']
+ if bld.env.DEST_OS == 'darwin':
+ defines += ['HAVE_SPAWN_H=1', 'HAVE_POSIX_SPAWN=1', 'HAVE_POSIX_SPAWNP=1']
bld.stlib(target = 'iberty',
features = 'c',
install_path = None,
includes = ['libiberty'],
cflags = conf['cflags'],
- defines = ['HAVE_CONFIG_H=1'],
+ defines = defines,
source =['libiberty/concat.c',
'libiberty/cplus-dem.c',
'libiberty/cp-demangle.c',
@@ -418,11 +421,11 @@ def bld_libiberty(bld, conf):
'libiberty/stpcpy.c',
'libiberty/pex-common.c',
'libiberty/pex-one.c',
+ 'libiberty/xexit.c',
'libiberty/xmalloc.c',
'libiberty/xmemdup.c',
'libiberty/xstrdup.c',
- 'libiberty/xstrerror.c',
- pex_host])
+ 'libiberty/xstrerror.c'] + pex_host)
#
# The doxy command.
diff --git a/tester/covoar/ConfigFile.cc b/tester/covoar/ConfigFile.cc
index c16b64a..1fbefd1 100644
--- a/tester/covoar/ConfigFile.cc
+++ b/tester/covoar/ConfigFile.cc
@@ -7,14 +7,21 @@
#include "ConfigFile.h"
#include <string.h>
-#include <stdio.h>
#include <ctype.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+static void print_invalid_line_number( const std::string& file, int line_no )
+{
+ std::cerr << file << ": line" << line_no << " is invalid: " << line
+ << std::endl;
+}
+
namespace Configuration {
- FileReader::FileReader(
- Options_t *options
- )
+ FileReader::FileReader( Options_t *options )
{
options_m = options;
}
@@ -23,44 +30,40 @@ namespace Configuration {
{
}
- bool FileReader::processFile(
- const char* const file
- )
+ bool FileReader::processFile( const std::string& file )
{
#define METHOD "FileReader::processFile - "
- FILE * in;
- char line[256];
- char option[256];
- char value[256];
- int line_no;
- int i;
- int j;
-
- if ( file == NULL ) {
- fprintf( stderr, METHOD "NULL filename\n" );
+ #define MAX_LENGTH 256
+
+ std::ifstream in;
+ std::string line;
+ char option[MAX_LENGTH];
+ char value[MAX_LENGTH];
+ int line_no;
+ int i;
+ int j;
+
+ if ( file.empty() ) {
+ std::cerr << METHOD << "Empty filename" << std::endl;
return false;
}
- in = fopen( file, "r" );
- if ( !in ) {
- fprintf( stderr, METHOD "unable to open %s\n", file );
+ in.open( file );
+ if ( !in.is_open() ) {
+ std::cerr << METHOD << "unable to open " << file << std::endl;
return false;
}
line_no = 0;
- while (fgets(line, sizeof(line), in) != NULL) {
+ while ( std::getline( line, MAX_LENGTH ) ) {
int length;
line_no++;
- length = (int) strlen( line );
- if ( line[length - 1] != '\n' ) {
- fprintf(
- stderr,
- "%s: line %d is too long",
- file,
- line_no
- );
+ length = (int) line.length();
+ if ( length > MAX_LENGTH ) {
+ std::cerr << file << ": line " << line_no << " is too long"
+ << std::endl;
continue;
}
@@ -72,7 +75,7 @@ namespace Configuration {
*
* LHS = RHS # comment
*/
- for (i=0 ; i<length ; i++ ) {
+ for ( i = 0; i < length; i++ ) {
if ( line[i] == '#' ) {
line[i] = '\0';
length = i;
@@ -83,70 +86,51 @@ namespace Configuration {
/*
* Strip off trailing white space
*/
- for (i=length-1 ; i>=0 && isspace(line[i]) ; i-- )
+ for ( i = length - 1; i >= 0 && isspace( line[i] ); i-- )
;
line[i+1] = '\0';
- length = i+1;
+ length = i + 1;
/* Ignore empty lines. We have stripped
* all comments and blanks therefore, only
* an empty string needs to be checked.
*/
- if (line[0] == '\0')
+ if ( line[0] == '\0' ) {
continue;
+ }
- if (sscanf(line, "%s", option) != 1) {
- fprintf(
- stderr,
- "%s: line %d is invalid: %s\n",
- file,
- line_no,
- line
- );
+ if ( std::sscanf( line.c_str(), "%s", option ) != 1 ) {
+ print_invalid_line_number( file, line_no );
continue;
}
- for (i=0; ((line[i] != '=') && (i<length)); i++)
+ for ( i=0; ( ( line[i] != '=' ) && ( i < length ) ); i++ )
;
- if (i == length) {
- fprintf(
- stderr,
- "%s: line %d is invalid: %s\n",
- file,
- line_no,
- line
- );
+ if ( i == length ) {
+ print_invalid_line_number( file, line_no );
continue;
}
i++;
value[0] = '\0';
- while ( isspace(line[i]) )
+ while ( isspace( line[i] ) ) {
i++;
- for (j=0; line[i] != '\0'; i++, j++ )
+ }
+
+ for ( j = 0; line[i] != '\0'; i++, j++ ) {
value[j] = line[i];
+ }
+
value[j] = '\0';
- if (value[0] == '\0') {
- fprintf(
- stderr,
- "%s: line %d is invalid: %s\n",
- file,
- line_no,
- line
- );
+ if ( value[0] == '\0' ) {
+ print_invalid_line_number( file, line_no );
continue;
}
- if ( !setOption(option, value) ) {
- fprintf(
- stderr,
- "%s: line %d: option %s is unknown\n",
- file,
- line_no,
- option
- );
+ if ( !setOption( option, value ) ) {
+ print_invalid_line_number( file, line_no );
continue;
}
@@ -160,37 +144,37 @@ namespace Configuration {
const char* const value
)
{
- Options_t *o;
+ Options_t* o;
- for ( o=options_m ; o->option ; o++ ) {
+ for ( o = options_m; o->option; o++ ) {
if ( !strcmp( o->option, option ) ) {
o->value = strdup( value );
return true;
}
}
+
return false;
}
- const char *FileReader::getOption(
- const char* const option
- )
+ const char *FileReader::getOption( const char* const option )
{
Options_t *o;
- for ( o=options_m ; o->option ; o++ ) {
+ for ( o = options_m; o->option; o++ ) {
if ( !strcmp( o->option, option ) ) {
return o->value;
}
}
+
return NULL;
}
- void FileReader::printOptions(void)
+ void FileReader::printOptions()
{
Options_t *o;
- for ( o=options_m ; o->option ; o++ ) {
- fprintf( stderr, "(%s)=(%s)\n", o->option, o->value );
+ for ( o = options_m; o->option; o++ ) {
+ std::cerr << '(' << o->option << ")=(" << o->value << ')' << std::endl;
}
}
}
diff --git a/tester/covoar/ConfigFile.h b/tester/covoar/ConfigFile.h
index 0bae7ac..0339c12 100644
--- a/tester/covoar/ConfigFile.h
+++ b/tester/covoar/ConfigFile.h
@@ -53,9 +53,7 @@ namespace Configuration {
*
* @return Returns TRUE if the method succeeded and FALSE if it failed.
*/
- virtual bool processFile(
- const char* const file
- );
+ virtual bool processFile( const std::string& file );
bool setOption(
const char* const option,
@@ -66,7 +64,7 @@ namespace Configuration {
const char* const option
);
- void printOptions(void);
+ void printOptions();
private:
/*!
@@ -78,7 +76,7 @@ namespace Configuration {
*
* @return Returns TRUE if the method succeeded and FALSE if it failed.
*/
- Options_t *options_m;
+ Options_t* options_m;
};
diff --git a/tester/covoar/ExecutableInfo.cc b/tester/covoar/ExecutableInfo.cc
index 418a7f4..5f8584e 100644
--- a/tester/covoar/ExecutableInfo.cc
+++ b/tester/covoar/ExecutableInfo.cc
@@ -21,74 +21,81 @@ namespace Coverage {
const std::string& theLibraryName,
bool verbose,
DesiredSymbols& symbolsToAnalyze
- ) : fileName(theExecutableName),
- loadAddress(0),
- symbolsToAnalyze_m(symbolsToAnalyze)
+ ) : fileName( theExecutableName ),
+ loadAddress( 0 ),
+ symbolsToAnalyze_m( symbolsToAnalyze )
{
- if ( !theLibraryName.empty() )
+ if ( !theLibraryName.empty() ) {
libraryName = theLibraryName;
+ }
- if (verbose) {
+ if ( verbose ) {
std::cerr << "Loading executable " << theExecutableName;
- if ( !theLibraryName.empty() )
+ if ( !theLibraryName.empty() ) {
std::cerr << " (" << theLibraryName << ')';
+ }
+
std::cerr << std::endl;
}
- rld::files::object executable(theExecutableName);
+ rld::files::object executable( theExecutableName );
executable.open();
executable.begin();
- executable.load_symbols(symbols);
+ executable.load_symbols( symbols );
rld::dwarf::file debug;
- debug.begin(executable.elf());
+ debug.begin( executable.elf() );
debug.load_debug();
debug.load_functions();
- for (auto& cu : debug.get_cus()) {
- AddressLineRange& range = mapper.makeRange(cu.pc_low(), cu.pc_high());
+ for ( auto& cu : debug.get_cus() ) {
+ AddressLineRange& range = mapper.makeRange( cu.pc_low(), cu.pc_high() );
// Does not filter on desired symbols under the assumption that the test
// code and any support code is small relative to what is being tested.
- for (const auto &address : cu.get_addresses()) {
- range.addSourceLine(address);
+ for ( const auto &address : cu.get_addresses() ) {
+ range.addSourceLine( address );
}
- for (auto& func : cu.get_functions()) {
- if (!func.has_machine_code()) {
+ for ( auto& func : cu.get_functions() ) {
+ if ( !func.has_machine_code() ) {
continue;
}
- if (!symbolsToAnalyze_m.isDesired(func.name())) {
+ if ( !symbolsToAnalyze_m.isDesired( func.name() ) ) {
continue;
}
- if (func.is_inlined()) {
- if (func.is_external()) {
+ if ( func.is_inlined() ) {
+ if ( func.is_external() ) {
// Flag it
std::cerr << "Function is both external and inlined: "
<< func.name() << std::endl;
}
- if (func.has_entry_pc()) {
+ if ( func.has_entry_pc() ) {
continue;
}
// If the low PC address is zero, the symbol does not appear in
// this executable.
- if (func.pc_low() == 0) {
+ if ( func.pc_low() == 0 ) {
continue;
}
}
// We can't process a zero size function.
- if (func.pc_high() == 0) {
+ if ( func.pc_high() == 0 ) {
continue;
}
- createCoverageMap (cu.name(), func.name(),
- func.pc_low(), func.pc_high() - 1);
+ createCoverageMap(
+ cu.name(),
+ func.name(),
+ func.pc_low(),
+ func.pc_high() - 1
+ );
}
}
}
@@ -97,16 +104,18 @@ namespace Coverage {
{
}
- void ExecutableInfo::dumpCoverageMaps( void ) {
- ExecutableInfo::CoverageMaps::iterator itr;
+ void ExecutableInfo::dumpCoverageMaps()
+ {
+ ExecutableInfo::CoverageMaps::iterator itr;
- for (auto& cm : coverageMaps) {
+ for ( auto& cm : coverageMaps ) {
std::cerr << "Coverage Map for " << cm.first << std::endl;
cm.second->dump();
}
}
- void ExecutableInfo::dumpExecutableInfo( void ){
+ void ExecutableInfo::dumpExecutableInfo()
+ {
std::cout << std::endl
<< "== Executable info ==" << std::endl
<< "executable = " << getFileName () << std::endl
@@ -115,7 +124,7 @@ namespace Coverage {
theSymbolTable.dumpSymbolTable();
}
- CoverageMapBase* ExecutableInfo::getCoverageMap ( uint32_t address )
+ CoverageMapBase* ExecutableInfo::getCoverageMap( uint32_t address )
{
CoverageMapBase* aCoverageMap = NULL;
CoverageMaps::iterator it;
@@ -123,29 +132,29 @@ namespace Coverage {
// Obtain the coverage map containing the specified address.
itsSymbol = theSymbolTable.getSymbol( address );
- if (itsSymbol != "") {
- aCoverageMap = &findCoverageMap(itsSymbol);
+ if ( itsSymbol != "" ) {
+ aCoverageMap = &findCoverageMap( itsSymbol );
}
return aCoverageMap;
}
- const std::string& ExecutableInfo::getFileName ( void ) const
+ const std::string& ExecutableInfo::getFileName() const
{
return fileName;
}
- const std::string ExecutableInfo::getLibraryName( void ) const
+ const std::string ExecutableInfo::getLibraryName() const
{
return libraryName;
}
- uint32_t ExecutableInfo::getLoadAddress( void ) const
+ uint32_t ExecutableInfo::getLoadAddress() const
{
return loadAddress;
}
- SymbolTable* ExecutableInfo::getSymbolTable ( void )
+ SymbolTable* ExecutableInfo::getSymbolTable()
{
return &theSymbolTable;
}
@@ -155,8 +164,10 @@ namespace Coverage {
)
{
CoverageMaps::iterator cmi = coverageMaps.find( symbolName );
- if ( cmi == coverageMaps.end() )
- throw CoverageMapNotFoundError(symbolName);
+ if ( cmi == coverageMaps.end() ) {
+ throw CoverageMapNotFoundError( symbolName );
+ }
+
return *(cmi->second);
}
@@ -167,8 +178,8 @@ namespace Coverage {
uint32_t highAddress
)
{
- CoverageMapBase *theMap;
- CoverageMaps::iterator itr;
+ CoverageMapBase* theMap;
+ CoverageMaps::iterator itr;
if ( lowAddress > highAddress ) {
std::ostringstream what;
@@ -195,22 +206,24 @@ namespace Coverage {
std::string& line
)
{
- std::string file;
- int lno;
- mapper.getSource (address, file, lno);
+ std::string file;
+ int lno;
std::ostringstream ss;
+
+ mapper.getSource( address, file, lno );
ss << file << ':' << lno;
line = ss.str ();
}
- bool ExecutableInfo::hasDynamicLibrary( void )
+ bool ExecutableInfo::hasDynamicLibrary()
{
return !libraryName.empty();
}
- void ExecutableInfo::mergeCoverage( void ) {
- for (auto& cm : coverageMaps) {
- if (symbolsToAnalyze_m.isDesired( cm.first ))
+ void ExecutableInfo::mergeCoverage()
+ {
+ for ( auto& cm : coverageMaps ) {
+ if ( symbolsToAnalyze_m.isDesired( cm.first ) )
symbolsToAnalyze_m.mergeCoverageMap( cm.first, cm.second );
}
}
diff --git a/tester/covoar/ExecutableInfo.h b/tester/covoar/ExecutableInfo.h
index 0adebcb..34f023d 100644
--- a/tester/covoar/ExecutableInfo.h
+++ b/tester/covoar/ExecutableInfo.h
@@ -62,12 +62,12 @@ class DesiredSymbols;
* This method prints the contents of all coverage maps for
* this executable.
*/
- void dumpCoverageMaps( void );
+ void dumpCoverageMaps();
/*!
* This method prints the contents of Executable info containers
*/
- void dumpExecutableInfo( void );
+ void dumpExecutableInfo();
/*!
* This method returns a pointer to the executable's coverage map
@@ -84,28 +84,28 @@ class DesiredSymbols;
*
* @return Returns the executable's file name
*/
- const std::string& getFileName( void ) const;
+ const std::string& getFileName() const;
/*!
* This method returns the library name associated with the executable.
*
* @return Returns the executable's library name
*/
- const std::string getLibraryName( void ) const;
+ const std::string getLibraryName() const;
/*!
* This method returns the load address of the dynamic library
*
* @return Returns the load address of the dynamic library
*/
- uint32_t getLoadAddress( void ) const;
+ uint32_t getLoadAddress() const;
/*!
* This method returns a pointer to the executable's symbol table.
*
* @return Returns a pointer to the symbol table.
*/
- SymbolTable* getSymbolTable( void );
+ SymbolTable* getSymbolTable();
/*!
* This method finds a coverage map for the specified symbol.
@@ -131,13 +131,13 @@ class DesiredSymbols;
*
* @return Returns TRUE if
*/
- bool hasDynamicLibrary( void );
+ bool hasDynamicLibrary();
/*!
* This method merges the coverage maps for this executable into
* the unified coverage map.
*/
- void mergeCoverage( void );
+ void mergeCoverage();
/*!
* This method sets the load address of the dynamic library
diff --git a/tester/covoar/Explanations.cc b/tester/covoar/Explanations.cc
index 4142418..d9d7a4f 100644
--- a/tester/covoar/Explanations.cc
+++ b/tester/covoar/Explanations.cc
@@ -26,19 +26,18 @@ namespace Coverage {
{
}
- void Explanations::load(
- const char* const explanations
- )
+ void Explanations::load( const std::string& explanations )
{
- std::ifstream explain;
- Explanation e;
- int line = 1;
+ std::ifstream explain;
+ Explanation e;
+ int line = 1;
- if (!explanations)
+ if ( explanations.empty() ) {
return;
+ }
explain.open( explanations );
- if (!explain) {
+ if ( !explain ) {
std::ostringstream what;
what << "Unable to open " << explanations;
throw rld::error( what, "Explanations::load" );
@@ -50,14 +49,15 @@ namespace Coverage {
// skip blank lines between
do {
std::getline( explain, input_line );
- if (explain.fail()) {
+ if ( explain.fail() ) {
return;
}
+
line++;
} while ( input_line.empty() );
// Have we already seen this one?
- if (set.find( input_line ) != set.end()) {
+ if ( set.find( input_line ) != set.end() ) {
std::ostringstream what;
what << "line " << line
<< "contains a duplicate explanation ("
@@ -71,7 +71,7 @@ namespace Coverage {
// Get the classification
std::getline( explain, input_line );
- if (explain.fail()) {
+ if ( explain.fail() ) {
std::ostringstream what;
what << "line " << line
<< "out of sync at the classification";
@@ -85,14 +85,14 @@ namespace Coverage {
line++;
const std::string delimiter = "+++";
- if (input_line.compare( delimiter ) == 0) {
+ if ( input_line.compare( delimiter ) == 0 ) {
break;
}
// XXX only taking last line. Needs to be a vector
e.explanation.push_back( input_line );
}
- if (explain.fail()) {
+ if ( explain.fail() ) {
std::ostringstream what;
what << "line " << line
<< "out of sync at the explanation";
@@ -109,7 +109,7 @@ namespace Coverage {
const std::string& start
)
{
- if (set.find( start ) == set.end()) {
+ if ( set.find( start ) == set.end() ) {
#if 0
std::cerr << "Warning: Unable to find explanation for "
<< start << std::endl;
@@ -120,38 +120,39 @@ namespace Coverage {
return &set[ start ];
}
- void Explanations::writeNotFound(
- const char* const fileName
- )
+ void Explanations::writeNotFound( const std::string& fileName )
{
std::ofstream notFoundFile;
bool notFoundOccurred = false;
- if (!fileName)
+ if ( fileName.empty() ) {
return;
+ }
notFoundFile.open( fileName );
- if (!notFoundFile) {
+ if ( !notFoundFile ) {
std::ostringstream what;
what << "Unable to open " << fileName
<< " out of sync at the explanation";
throw rld::error( what, "Explanations::writeNotFound" );
}
- for (std::map<std::string, Explanation>::iterator itr = set.begin();
- itr != set.end();
- itr++) {
+ for (
+ std::map<std::string, Explanation>::iterator itr = set.begin();
+ itr != set.end();
+ itr++
+ ) {
Explanation e = (*itr).second;
std::string key = (*itr).first;
- if (!e.found) {
+ if ( !e.found ) {
notFoundOccurred = true;
notFoundFile << e.startingPoint << std::endl;
}
}
- if (!notFoundOccurred) {
- if (!unlink( fileName )) {
+ if ( !notFoundOccurred ) {
+ if ( !unlink( fileName.c_str() ) ) {
std::cerr << "Warning: Unable to unlink " << fileName
<< std::endl
<< std::endl;
diff --git a/tester/covoar/Explanations.h b/tester/covoar/Explanations.h
index de0c051..c147c11 100644
--- a/tester/covoar/Explanations.h
+++ b/tester/covoar/Explanations.h
@@ -49,7 +49,7 @@ namespace Coverage {
/*!
* This method constructs an Explanation instance.
*/
- Explanation() {found = false;}
+ Explanation() { found = false; }
/*!
* This method destructs an Explanation instance.
@@ -87,9 +87,7 @@ namespace Coverage {
* @param[in] explanations specifies the file name containing
* the explanation information
*/
- void load(
- const char* const explanations
- );
+ void load( const std::string& explanations );
/*!
* This method returns the explanation associated with the
@@ -98,9 +96,7 @@ namespace Coverage {
* @param[in] start specifies the starting line number for
* which to search
*/
- const Explanation *lookupExplanation(
- const std::string& start
- );
+ const Explanation *lookupExplanation( const std::string& start );
/*!
* This method writes a file that contains a list of any
@@ -108,9 +104,7 @@ namespace Coverage {
*
* @param[in] fileName specifies the name of the file to write
*/
- void writeNotFound(
- const char* const fileName
- );
+ void writeNotFound( const std::string& fileName );
};
diff --git a/tester/covoar/ObjdumpProcessor.cc b/tester/covoar/ObjdumpProcessor.cc
index c910046..0aef978 100644
--- a/tester/covoar/ObjdumpProcessor.cc
+++ b/tester/covoar/ObjdumpProcessor.cc
@@ -13,6 +13,8 @@
#include <string.h>
#include <algorithm>
#include <string>
+#include <fstream>
+#include <iomanip>
#include "ObjdumpProcessor.h"
#include "CoverageMap.h"
@@ -36,73 +38,81 @@ namespace Coverage {
) {
// Find the symbol's coverage map.
try {
- CoverageMapBase& coverageMap = executableInfo->findCoverageMap(symbolName);
+ CoverageMapBase& coverageMap =
+ executableInfo->findCoverageMap( symbolName );
uint32_t firstInstructionAddress = UINT32_MAX;
// Find the address of the first instruction.
- for (auto& line : instructions) {
- if (line.isInstruction) {
+ for ( auto& line : instructions ) {
+ if ( line.isInstruction ) {
firstInstructionAddress = line.address;
break;
}
}
- if (firstInstructionAddress == UINT32_MAX) {
+ if ( firstInstructionAddress == UINT32_MAX ) {
std::ostringstream what;
what << "Could not find first instruction address for symbol "
- << symbolName << " in " << executableInfo->getFileName();
+ << symbolName << " in " << executableInfo->getFileName();
throw rld::error( what, "Coverage::finalizeSymbol" );
}
- int rangeIndex = -1;
+ int rangeIndex = -1;
uint32_t lowAddress = UINT32_MAX;
do {
rangeIndex++;
- lowAddress = coverageMap.getLowAddressOfRange(rangeIndex);
- } while (firstInstructionAddress != lowAddress);
+ lowAddress = coverageMap.getLowAddressOfRange( rangeIndex );
+ } while ( firstInstructionAddress != lowAddress );
- uint32_t sizeWithoutNops = coverageMap.getSizeOfRange(rangeIndex);
- uint32_t size = sizeWithoutNops;
- uint32_t highAddress = lowAddress + size - 1;
+ uint32_t sizeWithoutNops = coverageMap.getSizeOfRange( rangeIndex );
+ uint32_t size = sizeWithoutNops;
+ uint32_t highAddress = lowAddress + size - 1;
uint32_t computedHighAddress = highAddress;
// Find the high address as reported by the address of the last NOP
// instruction. This ensures that NOPs get marked as executed later.
- for (auto instruction = instructions.rbegin();
- instruction != instructions.rend();
- instruction++) {
- if (instruction->isInstruction) {
- if (instruction->isNop) {
+ for (
+ auto instruction = instructions.rbegin();
+ instruction != instructions.rend();
+ instruction++
+ ) {
+ if ( instruction->isInstruction ) {
+ if ( instruction->isNop ) {
computedHighAddress = instruction->address + instruction->nopSize;
}
+
break;
}
}
- if (highAddress != computedHighAddress) {
- std::cerr << "Function's high address differs between DWARF and objdump: "
- << symbolName << " (0x" << std::hex << highAddress << " and 0x"
- << computedHighAddress - 1 << ")" << std::dec << std::endl;
+ if ( highAddress != computedHighAddress ) {
+ std::cerr << "Function's high address differs between DWARF and "
+ << "objdump: " << symbolName << " (0x" << std::hex
+ << highAddress << " and 0x"
+ << computedHighAddress - 1 << ")" << std::dec << std::endl;
+
size = computedHighAddress - lowAddress;
}
// If there are NOT already saved instructions, save them.
SymbolInformation* symbolInfo = symbolsToAnalyze.find( symbolName );
- if (symbolInfo->instructions.empty()) {
- symbolInfo->sourceFile = executableInfo;
- symbolInfo->baseAddress = lowAddress;
+ if ( symbolInfo->instructions.empty() ) {
+ symbolInfo->sourceFile = executableInfo;
+ symbolInfo->baseAddress = lowAddress;
symbolInfo->instructions = instructions;
}
// Add the symbol to this executable's symbol table.
SymbolTable* theSymbolTable = executableInfo->getSymbolTable();
theSymbolTable->addSymbol(
- symbolName, lowAddress, highAddress - lowAddress + 1
+ symbolName,
+ lowAddress,
+ highAddress - lowAddress + 1
);
// Mark the start of each instruction in the coverage map.
- for (auto& instruction : instructions) {
+ for ( auto& instruction : instructions ) {
coverageMap.setIsStartOfInstruction( instruction.address );
}
@@ -114,16 +124,16 @@ namespace Coverage {
sizeWithoutNops,
verbose
);
- } catch (const ExecutableInfo::CoverageMapNotFoundError& e) {
+ } catch ( const ExecutableInfo::CoverageMapNotFoundError& e ) {
// Allow execution to continue even if a coverage map could not be
// found.
std::cerr << "Coverage map not found for symbol " << e.what()
- << std::endl;
+ << std::endl;
}
}
ObjdumpProcessor::ObjdumpProcessor(
- DesiredSymbols& symbolsToAnalyze,
+ DesiredSymbols& symbolsToAnalyze,
std::shared_ptr<Target::TargetBase>& targetInfo
): symbolsToAnalyze_m( symbolsToAnalyze ),
targetInfo_m( targetInfo )
@@ -139,14 +149,14 @@ namespace Coverage {
)
{
#define METHOD "ERROR: ObjdumpProcessor::determineLoadAddress - "
- FILE* loadAddressFile = NULL;
- char* cStatus;
- uint32_t offset;
- char inputBuffer[MAX_LINE_LENGTH];
+ std::ifstream loadAddressFile;
+ uint32_t offset;
+ char inputBuffer[ MAX_LINE_LENGTH ];
// This method should only be call for a dynamic library.
- if (!theExecutable->hasDynamicLibrary())
+ if ( !theExecutable->hasDynamicLibrary() ) {
return 0;
+ }
std::string dlinfoName = theExecutable->getFileName();
uint32_t address;
@@ -155,8 +165,8 @@ namespace Coverage {
dlinfoName += ".dlinfo";
// Read load address.
- loadAddressFile = ::fopen( dlinfoName.c_str(), "r" );
- if (!loadAddressFile) {
+ loadAddressFile.open( dlinfoName );
+ if ( !loadAddressFile.is_open() ) {
std::ostringstream what;
what << "Unable to open " << dlinfoName;
throw rld::error( what, METHOD );
@@ -166,23 +176,26 @@ namespace Coverage {
while ( 1 ) {
// Get a line.
- cStatus = ::fgets( inputBuffer, MAX_LINE_LENGTH, loadAddressFile );
- if (cStatus == NULL) {
- ::fclose( loadAddressFile );
+ loadAddressFile.getline( inputBuffer, MAX_LINE_LENGTH );
+ if ( loadAddressFile.fail() && loadAddressFile.is_open() ) {
+ loadAddressFile.close();
std::ostringstream what;
what << "library " << Library << " not found in " << dlinfoName;
throw rld::error( what, METHOD );
}
+
sscanf( inputBuffer, "%s %x", inLibName, &offset );
std::string tmp = inLibName;
if ( tmp.find( Library ) != tmp.npos ) {
- // fprintf( stderr, "%s - 0x%08x\n", inLibName, offset );
+ // std::cerr << inLibName << " - 0x"
+ // << std::setfill( '0' ) << std::setw( 8 ) << std::hex
+ // << offset << std::endl
+ // << std::dec << std::setfill( ' ' );
address = offset;
break;
}
}
- ::fclose( loadAddressFile );
return address;
#undef METHOD
@@ -195,23 +208,21 @@ namespace Coverage {
stderr,
"ERROR: ObjdumpProcessor::IsBranch - unknown architecture\n"
);
- assert(0);
+ assert( 0 );
return false;
}
return targetInfo_m->isBranch( instruction );
}
- bool ObjdumpProcessor::isBranchLine(
- const char* const line
- )
+ bool ObjdumpProcessor::isBranchLine( const std::string& line )
{
if ( !targetInfo_m ) {
fprintf(
stderr,
"ERROR: ObjdumpProcessor::isBranchLine - unknown architecture\n"
);
- assert(0);
+ assert( 0 );
return false;
}
@@ -219,11 +230,11 @@ namespace Coverage {
}
bool ObjdumpProcessor::isNop(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
)
{
- if ( !targetInfo_m ){
+ if ( !targetInfo_m ) {
fprintf(
stderr,
"ERROR: ObjdumpProcessor::isNop - unknown architecture\n"
@@ -236,21 +247,32 @@ namespace Coverage {
}
void ObjdumpProcessor::getFile(
- std::string fileName,
+ std::string fileName,
rld::process::tempfile& objdumpFile,
rld::process::tempfile& err
- )
+ )
{
rld::process::status status;
- rld::process::arg_container args = { targetInfo_m->getObjdump(),
- "-Cda", "--section=.text", "--source",
- fileName };
+ rld::process::arg_container args = {
+ targetInfo_m->getObjdump(),
+ "-Cda",
+ "--section=.text",
+ "--source",
+ fileName
+ };
+
try
{
- status = rld::process::execute( targetInfo_m->getObjdump(),
- args, objdumpFile.name(), err.name() );
- if ( (status.type != rld::process::status::normal)
- || (status.code != 0) ) {
+ status = rld::process::execute(
+ targetInfo_m->getObjdump(),
+ args,
+ objdumpFile.name(),
+ err.name()
+ );
+ if (
+ ( status.type != rld::process::status::normal ) ||
+ ( status.code != 0 )
+ ) {
throw rld::error( "Objdump error", "generating objdump" );
}
} catch( rld::error& err )
@@ -269,12 +291,12 @@ namespace Coverage {
objdumpFile_t::iterator itr;
itr = find ( objdumpList.begin(), objdumpList.end(), address );
- if (itr == objdumpList.end()) {
+ if ( itr == objdumpList.end() ) {
return 0;
}
itr++;
- if (itr == objdumpList.end()) {
+ if ( itr == objdumpList.end() ) {
return 0;
}
@@ -283,21 +305,22 @@ namespace Coverage {
}
void ObjdumpProcessor::loadAddressTable (
- ExecutableInfo* const executableInformation,
- rld::process::tempfile& objdumpFile,
- rld::process::tempfile& err
+ ExecutableInfo* const executableInformation,
+ rld::process::tempfile& objdumpFile,
+ rld::process::tempfile& err
)
{
- int items;
- uint32_t offset;
- char terminator;
- std::string line;
+ int items;
+ uint32_t offset;
+ char terminator;
+ std::string line;
// Obtain the objdump file.
- if ( !executableInformation->hasDynamicLibrary() )
+ if ( !executableInformation->hasDynamicLibrary() ) {
getFile( executableInformation->getFileName(), objdumpFile, err );
- else
+ } else {
getFile( executableInformation->getLibraryName(), objdumpFile, err );
+ }
// Process all lines from the objdump file.
while ( true ) {
@@ -309,14 +332,10 @@ namespace Coverage {
}
// See if it is the dump of an instruction.
- items = sscanf(
- line.c_str(),
- "%x%c",
- &offset, &terminator
- );
+ items = sscanf( line.c_str(), "%x%c", &offset, &terminator );
// If it looks like an instruction ...
- if ((items == 2) && (terminator == ':')) {
+ if ( ( items == 2 ) && ( terminator == ':' ) ) {
objdumpList.push_back(
executableInformation->getLoadAddress() + offset
);
@@ -325,42 +344,43 @@ namespace Coverage {
}
void ObjdumpProcessor::load(
- ExecutableInfo* const executableInformation,
- rld::process::tempfile& objdumpFile,
- rld::process::tempfile& err,
- bool verbose
+ ExecutableInfo* const executableInformation,
+ rld::process::tempfile& objdumpFile,
+ rld::process::tempfile& err,
+ bool verbose
)
{
- std::string currentSymbol = "";
- uint32_t instructionOffset;
- int items;
- int found;
- objdumpLine_t lineInfo;
- uint32_t offset;
- bool processSymbol = false;
- char symbol[ MAX_LINE_LENGTH ];
- char terminator1;
- char terminatorOne;
- char terminator2;
- objdumpLines_t theInstructions;
- char instruction[ MAX_LINE_LENGTH ];
- char ID[ MAX_LINE_LENGTH ];
- std::string call = "";
- std::string jumpTableID = "";
- std::string line = "";
+ std::string currentSymbol = "";
+ uint32_t instructionOffset;
+ int items;
+ int found;
+ objdumpLine_t lineInfo;
+ uint32_t offset;
+ bool processSymbol = false;
+ char symbol[ MAX_LINE_LENGTH ];
+ char terminator1;
+ char terminatorOne;
+ char terminator2;
+ objdumpLines_t theInstructions;
+ char instruction[ MAX_LINE_LENGTH ];
+ char ID[ MAX_LINE_LENGTH ];
+ std::string call = "";
+ std::string jumpTableID = "";
+ std::string line = "";
// Obtain the objdump file.
- if ( !executableInformation->hasDynamicLibrary() )
+ if ( !executableInformation->hasDynamicLibrary() ) {
getFile( executableInformation->getFileName(), objdumpFile, err );
- else
+ } else {
getFile( executableInformation->getLibraryName(), objdumpFile, err );
+ }
while ( true ) {
// Get the line.
objdumpFile.read_line( line );
if ( line.empty() ) {
// If we are currently processing a symbol, finalize it.
- if (processSymbol) {
+ if ( processSymbol ) {
finalizeSymbol(
executableInformation,
currentSymbol,
@@ -368,23 +388,23 @@ namespace Coverage {
verbose,
symbolsToAnalyze_m
);
- fprintf(
- stderr,
- "WARNING: ObjdumpProcessor::load - analysis of symbol %s \n"
- " may be incorrect. It was the last symbol in %s\n"
- " and the length of its last instruction is assumed "
- " to be one.\n",
- currentSymbol.c_str(),
- executableInformation->getFileName().c_str()
- );
+
+ std::cerr << "WARNING: ObjdumpProcessor::load - analysis of symbol "
+ << currentSymbol << std::endl
+ << " may be incorrect. It was the last symbol in "
+ << executableInformation->getFileName() << std::endl
+ << " and the length of its last instruction"
+ << " is assumed to be one."
+ << std::endl;
}
+
objdumpFile.close();
break;
}
// Remove any extra line break
- if (line.back() == '\n') {
- line.erase(line.end() - 1);
+ if ( line.back() == '\n' ) {
+ line.erase( line.end() - 1 );
}
lineInfo.line = line;
@@ -402,22 +422,28 @@ namespace Coverage {
items = sscanf(
line.c_str(),
"%x <%[^>]>%c",
- &offset, symbol, &terminator1
+ &offset,
+ symbol,
+ &terminator1
);
// See if it is a jump table.
found = sscanf(
line.c_str(),
"%x%c\t%*[^\t]%c%s %*x %*[^+]%s",
- &instructionOffset, &terminatorOne, &terminator2, instruction, ID
+ &instructionOffset,
+ &terminatorOne,
+ &terminator2,
+ instruction,
+ ID
);
call = instruction;
jumpTableID = ID;
// If all items found, we are at the beginning of a symbol's objdump.
- if ((items == 3) && (terminator1 == ':')) {
+ if ( ( items == 3 ) && ( terminator1 == ':' ) ) {
// If we are currently processing a symbol, finalize it.
- if (processSymbol) {
+ if ( processSymbol ) {
finalizeSymbol(
executableInformation,
currentSymbol,
@@ -442,24 +468,27 @@ namespace Coverage {
// When this happens, the compiler will generate a function with a
// ".part.n" suffix. For our purposes, this generated function part is
// equivalent to the original function and should be treated as such.
- char *periodIndex = strstr(symbol, ".");
- if (periodIndex != NULL) {
+ char *periodIndex = strstr( symbol, "." );
+ if ( periodIndex != NULL ) {
*periodIndex = 0;
}
// See if the new symbol is one that we care about.
- if (symbolsToAnalyze_m.isDesired( symbol )) {
+ if ( symbolsToAnalyze_m.isDesired( symbol ) ) {
currentSymbol = symbol;
processSymbol = true;
theInstructions.push_back( lineInfo );
}
}
// If it looks like a jump table, finalize the symbol.
- else if ( (found == 5) && (terminatorOne == ':') && (terminator2 == '\t')
- && (call.find( "call" ) != std::string::npos)
- && (jumpTableID.find( "+0x" ) != std::string::npos)
- && processSymbol )
- {
+ else if (
+ ( found == 5 ) &&
+ ( terminatorOne == ':' ) &&
+ ( terminator2 == '\t' ) &&
+ ( call.find( "call" ) != std::string::npos ) &&
+ ( jumpTableID.find( "+0x" ) != std::string::npos ) &&
+ processSymbol
+ ) {
// If we are currently processing a symbol, finalize it.
if ( processSymbol ) {
finalizeSymbol(
@@ -470,19 +499,26 @@ namespace Coverage {
symbolsToAnalyze_m
);
}
+
processSymbol = false;
}
- else if (processSymbol) {
+ else if ( processSymbol ) {
// See if it is the dump of an instruction.
items = sscanf(
line.c_str(),
"%x%c\t%*[^\t]%c",
- &instructionOffset, &terminator1, &terminator2
+ &instructionOffset,
+ &terminator1,
+ &terminator2
);
// If it looks like an instruction ...
- if ((items == 3) && (terminator1 == ':') && (terminator2 == '\t')) {
+ if (
+ ( items == 3 ) &&
+ ( terminator1 == ':' ) &&
+ ( terminator2 == '\t' )
+ ) {
// update the line's information, save it and ...
lineInfo.address =
executableInformation->getLoadAddress() + instructionOffset;
diff --git a/tester/covoar/ObjdumpProcessor.h b/tester/covoar/ObjdumpProcessor.h
index 05a667f..b3c7262 100644
--- a/tester/covoar/ObjdumpProcessor.h
+++ b/tester/covoar/ObjdumpProcessor.h
@@ -91,7 +91,7 @@ namespace Coverage {
* This method constructs an ObjdumpProcessor instance.
*/
ObjdumpProcessor(
- DesiredSymbols& symbolsToAnalyze,
+ DesiredSymbols& symbolsToAnalyze,
std::shared_ptr<Target::TargetBase>& targetInfo
);
@@ -100,24 +100,24 @@ namespace Coverage {
*/
virtual ~ObjdumpProcessor();
- uint32_t determineLoadAddress(
- ExecutableInfo* theExecutable
- );
+ uint32_t determineLoadAddress( ExecutableInfo* theExecutable );
/*!
* This method fills a tempfile with the .text section of objdump
* for the given file name.
*/
- void getFile( std::string fileName,
- rld::process::tempfile& dmp,
- rld::process::tempfile& err );
+ void getFile(
+ std::string fileName,
+ rld::process::tempfile& dmp,
+ rld::process::tempfile& err
+ );
/*!
* This method fills the objdumpList list with all the
* instruction addresses in the object dump file.
*/
void loadAddressTable (
- ExecutableInfo* const executableInformation,
+ ExecutableInfo* const executableInformation,
rld::process::tempfile& dmp,
rld::process::tempfile& err
);
@@ -150,9 +150,7 @@ namespace Coverage {
* the given line in the objdmp file is a branch instruction,
* otherwise it returns false.
*/
- bool isBranchLine(
- const char* const line
- );
+ bool isBranchLine( const std::string& line );
/*!
* This method sets the targetInfo_m variable.
@@ -167,7 +165,7 @@ namespace Coverage {
* This variable consists of a list of all instruction addresses
* extracted from the obj dump file.
*/
- objdumpFile_t objdumpList;
+ objdumpFile_t objdumpList;
/*!
* This method determines whether the specified line is a
@@ -179,8 +177,8 @@ namespace Coverage {
* @return Returns TRUE if the instruction is a nop, FALSE otherwise.
*/
bool isNop(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
);
/*!
diff --git a/tester/covoar/ReportsBase.cc b/tester/covoar/ReportsBase.cc
index 219e5af..eb56c4f 100644
--- a/tester/covoar/ReportsBase.cc
+++ b/tester/covoar/ReportsBase.cc
@@ -65,7 +65,7 @@ void ReportsBase::OpenFile(
// Create the output directory if it does not already exist
#ifdef _WIN32
- sc = _mkdir( symbolSetOutputDirectory );
+ sc = _mkdir( symbolSetOutputDirectory.c_str() );
#else
sc = mkdir( symbolSetOutputDirectory.c_str(), 0755 );
#endif
@@ -591,80 +591,75 @@ void GenerateReports(
bool branchInfoAvailable
)
{
- typedef std::list<ReportsBase *> reportList_t;
+ using reportList_ptr = std::unique_ptr<ReportsBase>;
+ using reportList = std::vector<reportList_ptr>;
- reportList_t reportList;
- reportList_t::iterator ritr;
+ reportList reports;
std::string reportName;
- ReportsBase* reports;
time_t timestamp;
timestamp = time( NULL ); /* get current cal time */
- reports = new ReportsText(
- timestamp,
- symbolSetName,
- allExplanations,
- projectName,
- outputDirectory,
- symbolsToAnalyze,
- branchInfoAvailable
+ reports.emplace_back(
+ new ReportsText(
+ timestamp,
+ symbolSetName,
+ allExplanations,
+ projectName,
+ outputDirectory,
+ symbolsToAnalyze,
+ branchInfoAvailable
+ )
);
- reportList.push_back( reports );
- reports = new ReportsHtml(
- timestamp,
- symbolSetName,
- allExplanations,
- projectName,
- outputDirectory,
- symbolsToAnalyze,
- branchInfoAvailable
+ reports.emplace_back(
+ new ReportsHtml(
+ timestamp,
+ symbolSetName,
+ allExplanations,
+ projectName,
+ outputDirectory,
+ symbolsToAnalyze,
+ branchInfoAvailable
+ )
);
- reportList.push_back( reports );
- for ( ritr = reportList.begin(); ritr != reportList.end(); ritr++ ) {
- reports = *ritr;
+ for ( auto& report: reports ) {
- reportName = "index" + reports->ReportExtension();
+ reportName = "index" + report->ReportExtension();
if ( verbose ) {
std::cerr << "Generate " << reportName << std::endl;
}
- reports->WriteIndex( reportName );
+ report->WriteIndex( reportName );
- reportName = "annotated" + reports->ReportExtension();
+ reportName = "annotated" + report->ReportExtension();
if ( verbose ) {
std::cerr << "Generate " << reportName << std::endl;
}
- reports->WriteAnnotatedReport( reportName );
+ report->WriteAnnotatedReport( reportName );
- reportName = "branch" + reports->ReportExtension();
+ reportName = "branch" + report->ReportExtension();
if ( verbose ) {
std::cerr << "Generate " << reportName << std::endl;
}
- reports->WriteBranchReport( reportName );
+ report->WriteBranchReport( reportName );
- reportName = "uncovered" + reports->ReportExtension();
+ reportName = "uncovered" + report->ReportExtension();
if ( verbose ) {
std::cerr << "Generate " << reportName << std::endl;
}
- reports->WriteCoverageReport( reportName );
+ report->WriteCoverageReport( reportName );
- reportName = "sizes" + reports->ReportExtension();
+ reportName = "sizes" + report->ReportExtension();
if ( verbose ) {
std::cerr << "Generate " << reportName << std::endl;
}
- reports->WriteSizeReport( reportName );
+ report->WriteSizeReport( reportName );
- reportName = "symbolSummary" + reports->ReportExtension();
+ reportName = "symbolSummary" + report->ReportExtension();
if ( verbose ) {
std::cerr << "Generate " << reportName << std::endl;
}
- reports->WriteSymbolSummaryReport( reportName, symbolsToAnalyze );
- }
-
- for ( ritr = reportList.begin(); ritr != reportList.end(); ritr++ ) {
- reports = *ritr;
- delete reports;
+ report->WriteSymbolSummaryReport( reportName, symbolsToAnalyze );
}
ReportsBase::WriteSummaryReport(
diff --git a/tester/covoar/SymbolTable.cc b/tester/covoar/SymbolTable.cc
index cfbd7a3..a33a348 100644
--- a/tester/covoar/SymbolTable.cc
+++ b/tester/covoar/SymbolTable.cc
@@ -30,9 +30,9 @@ namespace Coverage {
const uint32_t length
)
{
- uint32_t end = 0;
- symbol_entry_t entry;
- symbolInfo_t symbolData;
+ uint32_t end = 0;
+ symbol_entry_t entry;
+ symbolInfo_t symbolData;
// Add an entry to the address map.
end = start + length - 1;
@@ -45,14 +45,14 @@ namespace Coverage {
symbolData.startingAddress = start;
symbolData.length = length;
- for (auto& symData : info[ symbol ]) {
+ for ( auto& symData : info[ symbol ] ) {
// The starting address could differ since we strip any suffixes beginning
// with a '.'
- if (symData.startingAddress != start) {
+ if ( symData.startingAddress != start ) {
continue;
}
- if (symData.length != length) {
+ if ( symData.length != length ) {
std::ostringstream what;
what << "Different lengths for the symbol "
<< symbol
@@ -66,39 +66,36 @@ namespace Coverage {
info[ symbol ].push_back( symbolData );
}
- SymbolTable::symbolInfo* SymbolTable::getInfo(
- const std::string& symbol
- )
+ SymbolTable::symbolInfo* SymbolTable::getInfo( const std::string& symbol )
{
info_t::iterator it = info.find( symbol );
- if (it == info.end())
+ if ( it == info.end() ) {
return NULL;
- else
- return (&(it->second));
+ } else {
+ return ( &(it->second) );
+ }
}
- uint32_t SymbolTable::getLength(
- const std::string& symbol
- )
+ uint32_t SymbolTable::getLength( const std::string& symbol )
{
info_t::iterator it = info.find( symbol );
- if (it == info.end())
+ if ( it == info.end() ) {
return 0;
- else
- return ((*it).second.front().length);
+ } else {
+ return ( (*it).second.front().length );
+ }
}
- std::string SymbolTable::getSymbol(
- uint32_t address
- )
+ std::string SymbolTable::getSymbol( uint32_t address )
{
contents_t::iterator it;
// Ensure that the symbol table is not empty.
- if ( contents.size() == 0 )
+ if ( contents.size() == 0 ) {
return "";
+ }
// Find the first entry whose end address is greater
// than the specified address.
@@ -106,23 +103,32 @@ namespace Coverage {
// If an entry was found and its low address is less than or
// equal to the specified address, then return the symbol.
- if ((it != contents.end()) && ((it->second).low <= address ))
+ if ( ( it != contents.end() ) && ( ( it->second ).low <= address ) ) {
return (it->second).symbol;
+ }
return "";
}
void SymbolTable::dumpSymbolTable( void )
{
- symbolInfo symbolTable;
- symbolInfoIterator_t symbolIterator;
- infoIterator_t infoIterator;
-
- for (infoIterator = info.begin() ; infoIterator != info.end(); infoIterator++)
- {
- for (symbolIterator = infoIterator->second.begin() ; symbolIterator != infoIterator->second.end(); symbolIterator++)
- {
- fprintf( stdout, "%s:\tStarting address = %#x\tLength = %u\n", infoIterator->first.c_str(), symbolIterator->startingAddress, symbolIterator->length );
+ symbolInfo symbolTable;
+ symbolInfoIterator_t symbolIterator;
+ infoIterator_t infoIterator;
+
+ for (
+ infoIterator = info.begin();
+ infoIterator != info.end();
+ infoIterator++
+ ) {
+ for (
+ symbolIterator = infoIterator->second.begin();
+ symbolIterator != infoIterator->second.end();
+ symbolIterator++
+ ) {
+ std::cerr << infoIterator->first << ":\tStarting address = 0x"
+ << std::hex << symbolIterator->startingAddress << std::dec
+ << "\tLength = " << symbolIterator->length << std::endl;
}
}
}
diff --git a/tester/covoar/TargetBase.cc b/tester/covoar/TargetBase.cc
index 7ee45b5..a62e90f 100644
--- a/tester/covoar/TargetBase.cc
+++ b/tester/covoar/TargetBase.cc
@@ -40,24 +40,24 @@ namespace Target {
{
}
- const char* TargetBase::getAddr2line() const
+ const std::string& TargetBase::getAddr2line() const
{
- return addr2line_m.c_str();
+ return addr2line_m;
}
- const char* TargetBase::getCPU( void ) const
+ const std::string& TargetBase::getCPU() const
{
- return cpu_m.c_str();
+ return cpu_m;
}
- const char* TargetBase::getObjdump() const
+ const std::string& TargetBase::getObjdump() const
{
- return objdump_m.c_str();
+ return objdump_m;
}
- const char* TargetBase::getTarget( void ) const
+ const std::string& TargetBase::getTarget() const
{
- return targetName_m.c_str();
+ return targetName_m;
}
bool TargetBase::isBranch( const std::string& instruction )
@@ -83,12 +83,13 @@ namespace Target {
}
bool TargetBase::isBranchLine(
- const char* const line
+ const std::string& line
)
{
- #define WARNING \
- "WARNING: TargetBase::isBranchLine - (%d) " \
- "Unable to find instruction in: %s\n"
+ #define WARNING_PT1 \
+ "WARNING: TargetBase::isBranchLine - ("
+ #define WARNING_PT2 \
+ ") Unable to find instruction in: "
const char *ch;
char instruction[120];
int result;
@@ -101,7 +102,7 @@ namespace Target {
ch++;
}
if (*ch != '\t') {
- fprintf( stderr, WARNING, 1, line );
+ std::cerr << WARNING_PT1 << 1 << WARNING_PT2 << line << std::endl;
return false;
}
ch++;
@@ -110,7 +111,7 @@ namespace Target {
while ((*ch != '\t') && (*ch != '\0'))
ch++;
if (*ch != '\t') {
- fprintf( stderr, WARNING, 2, line) ;
+ std::cerr << WARNING_PT1 << 2 << WARNING_PT2 << line << std::endl;
return false;
}
ch++;
@@ -119,19 +120,19 @@ namespace Target {
// after the second tab.
result = sscanf( ch, "%s", instruction );
if (result != 1) {
- fprintf( stderr, WARNING, 3, line );
+ std::cerr << WARNING_PT1 << 3 << WARNING_PT2 << line << std::endl;
return false;
}
return isBranch( instruction );
}
- uint8_t TargetBase::qemuTakenBit(void)
+ uint8_t TargetBase::qemuTakenBit()
{
return TRACE_OP_BR0;
}
- uint8_t TargetBase::qemuNotTakenBit(void)
+ uint8_t TargetBase::qemuNotTakenBit()
{
return TRACE_OP_BR1;
}
diff --git a/tester/covoar/TargetBase.h b/tester/covoar/TargetBase.h
index e5c143e..0a75c85 100644
--- a/tester/covoar/TargetBase.h
+++ b/tester/covoar/TargetBase.h
@@ -42,28 +42,28 @@ namespace Target {
*
* @return Returns the target specific addr2line program name
*/
- const char* getAddr2line( void ) const;
+ const std::string& getAddr2line() const;
/*!
* This method returns the CPU name.
*
* @return Returns the target cpu name
*/
- const char* getCPU( void ) const;
+ const std::string& getCPU() const;
/*!
* This method returns the program name for objdump.
*
* @return Returns the target specific objdump program name
*/
- const char* getObjdump( void ) const;
+ const std::string& getObjdump() const;
/*!
* This method returns the target name.
*
* @return Returns the target name
*/
- const char* getTarget( void ) const;
+ const std::string& getTarget() const;
/*!
* This method determines whether the specified line from a
@@ -75,8 +75,8 @@ namespace Target {
* @return Returns TRUE if the instruction is a nop, FALSE otherwise.
*/
virtual bool isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
) = 0;
@@ -90,7 +90,7 @@ namespace Target {
* @return Returns TRUE if the instruction is a branch, FALSE otherwise.
*/
bool isBranchLine(
- const char* const line
+ const std::string& line
);
@@ -104,13 +104,13 @@ namespace Target {
* This method returns the bit set by Qemu in the trace record
* when a branch is taken.
*/
- virtual uint8_t qemuTakenBit(void);
+ virtual uint8_t qemuTakenBit();
/*!
* This method returns the bit set by Qemu in the trace record
* when a branch is taken.
*/
- virtual uint8_t qemuNotTakenBit(void);
+ virtual uint8_t qemuNotTakenBit();
protected:
diff --git a/tester/covoar/TargetFactory.cc b/tester/covoar/TargetFactory.cc
index 57ba686..0b6be52 100644
--- a/tester/covoar/TargetFactory.cc
+++ b/tester/covoar/TargetFactory.cc
@@ -37,11 +37,9 @@ namespace Target {
//!
typedef struct {
//! This is the string found in configuration to match.
- const char *theTarget;
+ std::string theTarget;
//! This is the static wrapper for the constructor.
- TargetBase *(*theCtor)(
- std::string
- );
+ TargetBase *(*theCtor)( std::string );
} FactoryEntry_t;
//!
@@ -60,27 +58,27 @@ namespace Target {
{ "powerpc", Target_powerpc_Constructor },
{ "sparc", Target_sparc_Constructor },
{ "riscv", Target_riscv_Constructor },
- { "TBD", NULL },
+ { "TBD", NULL }
};
- TargetBase* TargetFactory(
- std::string targetName
- )
+ TargetBase* TargetFactory( std::string targetName )
{
size_t i;
std::string cpu;
i = targetName.find( '-' );
- if ( i == targetName.npos )
+ if ( i == targetName.npos ) {
cpu = targetName;
- else
+ } else {
cpu = targetName.substr( 0, i );
+ }
- // fprintf( stderr, "%s --> %s\n", targetName.c_str(), cpu.c_str());
+ // std::cerr << targetName << " --> " << cpu << std::endl;
// Iterate over the table trying to find an entry with a matching name
- for ( i=0 ; i < sizeof(FactoryTable) / sizeof(FactoryEntry_t); i++ ) {
- if ( !strcmp(FactoryTable[i].theTarget, cpu.c_str() ) )
+ for ( i = 0 ; i < sizeof( FactoryTable ) / sizeof( FactoryEntry_t ); i++) {
+ if ( FactoryTable[i].theTarget == cpu ) {
return FactoryTable[i].theCtor( targetName );
+ }
}
std::ostringstream what;
diff --git a/tester/covoar/Target_aarch64.cc b/tester/covoar/Target_aarch64.cc
index 4d16456..139f5fe 100644
--- a/tester/covoar/Target_aarch64.cc
+++ b/tester/covoar/Target_aarch64.cc
@@ -49,31 +49,33 @@ namespace Target {
}
bool Target_aarch64::isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
)
{
- if (!strcmp( &line[strlen(line)-3], "nop")) {
+ size_t stringLen = line.length();
+
+ if ( line.substr( stringLen - 3 ) == "nop" ) {
size = 4;
return true;
}
- if (!strncmp( &line[strlen(line)-6], "udf", 3)) {
+ if ( line.substr( stringLen - 6, 3 ) == "udf" ) {
size = 4;
return true;
}
// On ARM, there are literal tables at the end of methods.
// We need to avoid them.
- if (!strncmp( &line[strlen(line)-10], ".byte", 5)) {
+ if ( line.substr( stringLen - 10, 5 ) == ".byte" ) {
size = 1;
return true;
}
- if (!strncmp( &line[strlen(line)-13], ".short", 6)) {
+ if ( line.substr( stringLen - 13, 6 ) == ".short" ) {
size = 2;
return true;
}
- if (!strncmp( &line[strlen(line)-16], ".word", 5)) {
+ if ( line.substr( stringLen - 16, 5 ) == ".word" ) {
size = 4;
return true;
}
@@ -82,7 +84,7 @@ namespace Target {
}
bool Target_aarch64::isBranch(
- const char* instruction
+ const std::string& instruction
)
{
throw rld::error(
diff --git a/tester/covoar/Target_aarch64.h b/tester/covoar/Target_aarch64.h
index fb011e8..42353e5 100644
--- a/tester/covoar/Target_aarch64.h
+++ b/tester/covoar/Target_aarch64.h
@@ -42,8 +42,8 @@ namespace Target {
* @return Returns TRUE if the instruction is a nop, FALSE otherwise.
*/
virtual bool isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
) override;
/*!
@@ -51,7 +51,7 @@ namespace Target {
* objdump file is a branch instruction.
*/
bool isBranch(
- const char* const instruction
+ const std::string& instruction
);
/* Documentation inherited from base class */
diff --git a/tester/covoar/Target_arm.cc b/tester/covoar/Target_arm.cc
index 94d50bb..50c9ce3 100644
--- a/tester/covoar/Target_arm.cc
+++ b/tester/covoar/Target_arm.cc
@@ -82,26 +82,28 @@ namespace Target {
}
bool Target_arm::isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
)
{
- if (!strcmp( &line[strlen(line)-3], "nop")) {
+ size_t stringLen = line.length();
+
+ if ( line.substr( stringLen - 3 ) == "nop" ) {
size = 4;
return true;
}
// On ARM, there are literal tables at the end of methods.
// We need to avoid them.
- if (!strncmp( &line[strlen(line)-10], ".byte", 5)) {
+ if ( line.substr( stringLen - 10, 5 ) == ".byte" ) {
size = 1;
return true;
}
- if (!strncmp( &line[strlen(line)-13], ".short", 6)) {
+ if ( line.substr( stringLen - 13, 6 ) == ".short" ) {
size = 2;
return true;
}
- if (!strncmp( &line[strlen(line)-16], ".word", 5)) {
+ if ( line.substr( stringLen - 16, 5 ) == ".word" ) {
size = 4;
return true;
}
@@ -111,7 +113,7 @@ namespace Target {
}
bool Target_arm::isBranch(
- const char* instruction
+ const std::string& instruction
)
{
throw rld::error(
diff --git a/tester/covoar/Target_arm.h b/tester/covoar/Target_arm.h
index 80b68af..3ff9b6f 100644
--- a/tester/covoar/Target_arm.h
+++ b/tester/covoar/Target_arm.h
@@ -42,8 +42,8 @@ namespace Target {
* @return Returns TRUE if the instruction is a nop, FALSE otherwise.
*/
bool isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
);
/*!
@@ -51,7 +51,7 @@ namespace Target {
* objdump file is a branch instruction.
*/
bool isBranch(
- const char* const instruction
+ const std::string& instruction
);
private:
diff --git a/tester/covoar/Target_i386.cc b/tester/covoar/Target_i386.cc
index 2990273..cb8e64d 100644
--- a/tester/covoar/Target_i386.cc
+++ b/tester/covoar/Target_i386.cc
@@ -57,39 +57,41 @@ namespace Target {
}
bool Target_i386::isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
)
{
- if (!strcmp( &line[strlen(line)-3], "nop")) {
+ size_t stringLen = line.length();
+
+ if ( line.substr( stringLen - 3 ) == "nop" ) {
size = 1;
return true;
}
// i386 has some two and three byte nops
- if (!strncmp( &line[strlen(line)-14], "xchg %ax,%ax", 14)) {
+ if ( line.substr( stringLen - 14 ) == "xchg %ax,%ax" ) {
size = 2;
return true;
}
- if (!strncmp( &line[strlen(line)-16], "xor %eax,%eax", 16)) {
+ if ( line.substr( stringLen - 16 ) == "xor %eax,%eax" ) {
size = 2;
return true;
}
- if (!strncmp( &line[strlen(line)-16], "xor %ebx,%ebx", 16)) {
+ if ( line.substr( stringLen - 16 ) == "xor %ebx,%ebx" ) {
size = 2;
return true;
}
- if (!strncmp( &line[strlen(line)-16], "xor %esi,%esi", 16)) {
+ if ( line.substr( stringLen - 16 ) == "xor %esi,%esi" ) {
size = 2;
return true;
}
- if (!strncmp( &line[strlen(line)-21], "lea 0x0(%esi),%esi", 21)) {
+ if ( line.substr( stringLen - 21 ) == "lea 0x0(%esi),%esi" ) {
size = 3;
return true;
}
- if (!strncmp( &line[strlen(line)-28], "lea 0x0(%esi,%eiz,1),%esi", 28)) {
+ if ( line.substr( stringLen - 28 ) == "lea 0x0(%esi,%eiz,1),%esi" ) {
// Could be 4 or 7 bytes of padding.
- if (!strncmp( &line[strlen(line)-32], "00", 2)) {
+ if ( line.substr( stringLen - 32, 2 ) == "00" ) {
size = 7;
} else {
size = 4;
diff --git a/tester/covoar/Target_i386.h b/tester/covoar/Target_i386.h
index 3641de7..c9a79cd 100644
--- a/tester/covoar/Target_i386.h
+++ b/tester/covoar/Target_i386.h
@@ -43,8 +43,8 @@ namespace Target {
* @return Returns TRUE if the instruction is a nop, FALSE otherwise.
*/
bool isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
);
/* Documentation inherited from base class */
diff --git a/tester/covoar/Target_lm32.cc b/tester/covoar/Target_lm32.cc
index 18e7191..7babbd2 100644
--- a/tester/covoar/Target_lm32.cc
+++ b/tester/covoar/Target_lm32.cc
@@ -30,11 +30,11 @@ namespace Target {
}
bool Target_lm32::isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
)
{
- if (!strcmp( &line[strlen(line)-3], "nop")) {
+ if ( line.substr( line.length() - 3 ) == "nop" ) {
size = 4;
return true;
}
diff --git a/tester/covoar/Target_lm32.h b/tester/covoar/Target_lm32.h
index 5d23db5..d21776e 100644
--- a/tester/covoar/Target_lm32.h
+++ b/tester/covoar/Target_lm32.h
@@ -43,8 +43,8 @@ namespace Target {
* @return Returns TRUE if the instruction is a nop, FALSE otherwise.
*/
bool isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
);
private:
diff --git a/tester/covoar/Target_m68k.cc b/tester/covoar/Target_m68k.cc
index 536ae80..7c761be 100644
--- a/tester/covoar/Target_m68k.cc
+++ b/tester/covoar/Target_m68k.cc
@@ -77,11 +77,13 @@ namespace Target {
}
bool Target_m68k::isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
)
{
- if (!strcmp( &line[strlen(line)-3], "nop")) {
+ size_t stringLen = line.length();
+
+ if ( line.substr( stringLen - 3 ) == "nop" ) {
size = 2;
return true;
}
@@ -89,7 +91,7 @@ namespace Target {
#define GNU_LD_FILLS_ALIGNMENT_WITH_RTS
#if defined(GNU_LD_FILLS_ALIGNMENT_WITH_RTS)
// Until binutils 2.20, binutils would fill with rts not nop
- if (!strcmp( &line[strlen(line)-3], "rts")) {
+ if ( line.substr( stringLen - 3 ) == "rts" ) {
size = 4;
return true;
}
@@ -99,7 +101,7 @@ namespace Target {
}
bool Target_m68k::isBranch(
- const char* const instruction
+ const std::string& instruction
)
{
throw rld::error(
diff --git a/tester/covoar/Target_m68k.h b/tester/covoar/Target_m68k.h
index 5ed7933..13a6030 100644
--- a/tester/covoar/Target_m68k.h
+++ b/tester/covoar/Target_m68k.h
@@ -43,8 +43,8 @@ namespace Target {
* @return Returns TRUE if the instruction is a nop, FALSE otherwise.
*/
bool isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
);
/*!
@@ -52,7 +52,7 @@ namespace Target {
* objdump file is a branch instruction.
*/
bool isBranch(
- const char* const instruction
+ const std::string& instruction
);
/* Documentation inherited from base class */
diff --git a/tester/covoar/Target_powerpc.cc b/tester/covoar/Target_powerpc.cc
index c62feb0..6cc541d 100644
--- a/tester/covoar/Target_powerpc.cc
+++ b/tester/covoar/Target_powerpc.cc
@@ -58,11 +58,11 @@ namespace Target {
}
bool Target_powerpc::isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
)
{
- if (!strcmp( &line[strlen(line)-3], "nop")) {
+ if ( line.substr( line.length() - 3 ) == "nop" ) {
size = 4;
return true;
}
@@ -71,7 +71,7 @@ namespace Target {
}
bool Target_powerpc::isBranch(
- const char* const instruction
+ const std::string& instruction
)
{
throw rld::error(
diff --git a/tester/covoar/Target_powerpc.h b/tester/covoar/Target_powerpc.h
index 3008d4c..b446918 100644
--- a/tester/covoar/Target_powerpc.h
+++ b/tester/covoar/Target_powerpc.h
@@ -43,7 +43,7 @@ namespace Target {
* @return Returns TRUE if the instruction is a nop, FALSE otherwise.
*/
bool isNopLine(
- const char* const line,
+ const std::string& line,
int& size
);
@@ -52,7 +52,7 @@ namespace Target {
* objdump file is a branch instruction.
*/
bool isBranch(
- const char* const instruction
+ const std::string& instruction
);
private:
diff --git a/tester/covoar/Target_riscv.cc b/tester/covoar/Target_riscv.cc
index 13c81c5..d0b5a8e 100644
--- a/tester/covoar/Target_riscv.cc
+++ b/tester/covoar/Target_riscv.cc
@@ -62,11 +62,11 @@ namespace Target {
}
bool Target_riscv::isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
)
{
- if (!strcmp( &line[strlen(line)-3], "nop")){
+ if ( line.substr( line.length() - 3 ) == "nop" ) {
size = 4;
return true;
}
diff --git a/tester/covoar/Target_riscv.h b/tester/covoar/Target_riscv.h
index 64a63d4..1442f69 100644
--- a/tester/covoar/Target_riscv.h
+++ b/tester/covoar/Target_riscv.h
@@ -67,8 +67,8 @@ namespace Target {
* @return Returns True if the instruction is nop, False otherwise.
*/
bool isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
);
/*!
@@ -80,7 +80,7 @@ namespace Target {
*/
bool isBranch(
- const char* const instruction
+ const std::string& instruction
);
private:
diff --git a/tester/covoar/Target_sparc.cc b/tester/covoar/Target_sparc.cc
index fbdcd1e..a9435cb 100644
--- a/tester/covoar/Target_sparc.cc
+++ b/tester/covoar/Target_sparc.cc
@@ -56,23 +56,25 @@ namespace Target {
}
bool Target_sparc::isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
)
{
- if (!strcmp( &line[strlen(line)-3], "nop")) {
+ size_t stringLen = line.length();
+
+ if ( line.substr( stringLen - 3 ) == "nop" ) {
size = 4;
return true;
}
- if (!strcmp( &line[strlen(line)-7], "unknown")) {
+ if ( line.substr( stringLen - 7 ) == "unknown" ) {
size = 4;
return true;
}
#define GNU_LD_FILLS_ALIGNMENT_WITH_RTS
#if defined(GNU_LD_FILLS_ALIGNMENT_WITH_RTS)
// Until binutils 2.20, binutils would fill with rts not nop
- if (!strcmp( &line[strlen(line)-3], "rts")) {
+ if ( line.substr( stringLen - 3 ) == "rts" ) {
size = 4;
return true;
}
diff --git a/tester/covoar/Target_sparc.h b/tester/covoar/Target_sparc.h
index 0e38859..cbe2faf 100644
--- a/tester/covoar/Target_sparc.h
+++ b/tester/covoar/Target_sparc.h
@@ -43,8 +43,8 @@ namespace Target {
* @return Returns TRUE if the instruction is a nop, FALSE otherwise.
*/
bool isNopLine(
- const char* const line,
- int& size
+ const std::string& line,
+ int& size
);
/*!
@@ -52,7 +52,7 @@ namespace Target {
* objdump file is a branch instruction.
*/
bool isBranch(
- const char* const instruction
+ const std::string& instruction
);
private:
diff --git a/tester/covoar/TraceConverter.cc b/tester/covoar/TraceConverter.cc
index 7770b11..e393268 100644
--- a/tester/covoar/TraceConverter.cc
+++ b/tester/covoar/TraceConverter.cc
@@ -27,20 +27,18 @@
#define kill(p,s) raise(s)
#endif
-char* progname;
+std::string progname;
void usage()
{
- fprintf(
- stderr,
- "Usage: %s [-v] -c CPU -e executable -t tracefile [-E logfile]\n",
- progname
- );
- exit(1);
+ std::cerr << "Usage: "
+ << progname
+ << " [-v] -c CPU -e executable -t tracefile [-E logfile]"
+ << std::endl;
+ exit( 1 );
}
-static void
-fatal_signal( int signum )
+static void fatal_signal( int signum )
{
signal( signum, SIG_DFL );
@@ -53,20 +51,23 @@ fatal_signal( int signum )
kill( getpid(), signum );
}
-static void
-setup_signals( void )
+static void setup_signals()
{
- if ( signal (SIGINT, SIG_IGN) != SIG_IGN )
+ if ( signal (SIGINT, SIG_IGN) != SIG_IGN ) {
signal( SIGINT, fatal_signal );
+ }
#ifdef SIGHUP
- if ( signal( SIGHUP, SIG_IGN ) != SIG_IGN )
+ if ( signal( SIGHUP, SIG_IGN ) != SIG_IGN ) {
signal( SIGHUP, fatal_signal );
+ }
#endif
- if ( signal( SIGTERM, SIG_IGN ) != SIG_IGN )
+ if ( signal( SIGTERM, SIG_IGN ) != SIG_IGN ) {
signal( SIGTERM, fatal_signal );
+ }
#ifdef SIGPIPE
- if ( signal( SIGPIPE, SIG_IGN ) != SIG_IGN )
+ if ( signal( SIGPIPE, SIG_IGN ) != SIG_IGN ) {
signal( SIGPIPE, fatal_signal );
+ }
#endif
#ifdef SIGCHLD
signal( SIGCHLD, SIG_DFL );
@@ -81,18 +82,42 @@ int main(
int opt;
Trace::TraceReaderLogQEMU log;
Trace::TraceWriterQEMU trace;
- const char *cpuname = "";
- const char *executable = "";
- const char *tracefile = "";
- const char *logname = "/tmp/qemu.log";
+ std::string cpuname;
+ std::string executable;
+ std::string tracefile;
+ std::string logname = "/tmp/qemu.log";
Coverage::ExecutableInfo* executableInfo;
- rld::process::tempfile objdumpFile( ".dmp" );
- rld::process::tempfile err( ".err" );
Coverage::DesiredSymbols symbolsToAnalyze;
bool verbose = false;
std::string dynamicLibrary;
int ec = 0;
std::shared_ptr<Target::TargetBase> targetInfo;
+ rld::process::tempfile *objdumpFile;
+ rld::process::tempfile *err;
+
+ try
+ {
+ objdumpFile = new rld::process::tempfile( ".dmp" );
+ }
+ catch ( rld::error re )
+ {
+ std::cerr << "Failed to make .dmp tempfile " << std::endl;
+ ec = 10;
+
+ return ec;
+ }
+
+ try
+ {
+ err = new rld::process::tempfile( ".err" );
+ }
+ catch ( rld::error re )
+ {
+ std::cerr << "Failed to make .err tempfile " << std::endl;
+ ec = 10;
+
+ return ec;
+ }
setup_signals();
@@ -101,31 +126,31 @@ int main(
//
progname = argv[0];
- while ((opt = getopt(argc, argv, "c:e:l:L:t:v")) != -1) {
- switch (opt) {
- case 'c': cpuname = optarg; break;
- case 'e': executable = optarg; break;
- case 'l': logname = optarg; break;
+ while ( (opt = getopt( argc, argv, "c:e:l:L:t:v" ) ) != -1 ) {
+ switch ( opt ) {
+ case 'c': cpuname = optarg; break;
+ case 'e': executable = optarg; break;
+ case 'l': logname = optarg; break;
case 'L': dynamicLibrary = optarg; break;
- case 't': tracefile = optarg; break;
- case 'v': verbose = true; break;
- default: usage();
+ case 't': tracefile = optarg; break;
+ case 'v': verbose = true; break;
+ default: usage();
}
}
// Make sure we have all the required parameters
- if ( !cpuname ) {
- fprintf( stderr, "cpuname not specified\n" );
+ if ( cpuname.empty() ) {
+ std::cerr << "cpuname not specified" << std::endl;
usage();
}
- if ( !executable ) {
- fprintf( stderr, "executable not specified\n" );
+ if ( executable.empty() ) {
+ std::cerr << "executable not specified" << std::endl;
usage();
}
- if ( !tracefile ) {
- fprintf( stderr, "output trace file not specified\n" );
+ if ( tracefile.empty() ) {
+ std::cerr << "output trace file not specified" << std::endl;
usage();
}
@@ -147,18 +172,18 @@ int main(
Coverage::ObjdumpProcessor objdumpProcessor( symbolsToAnalyze, targetInfo );
- if ( !dynamicLibrary.empty() )
+ if ( !dynamicLibrary.empty() ) {
executableInfo = new Coverage::ExecutableInfo(
- executable,
+ executable.c_str(),
dynamicLibrary,
false,
symbolsToAnalyze
);
- else {
+ } else {
try
{
executableInfo = new Coverage::ExecutableInfo(
- executable,
+ executable.c_str(),
"",
false,
symbolsToAnalyze
@@ -192,9 +217,19 @@ int main(
}
}
- objdumpProcessor.loadAddressTable( executableInfo, objdumpFile, err );
- log.processFile( logname, objdumpProcessor );
- trace.writeFile( tracefile, &log, verbose );
+ try
+ {
+ objdumpProcessor.loadAddressTable( executableInfo, *objdumpFile, *err );
+ log.processFile( logname.c_str(), objdumpProcessor );
+ trace.writeFile( tracefile.c_str(), &log, verbose );
+ }
+ catch ( rld::error re )
+ {
+ std::cerr << "error: "
+ << re.where << ": " << re.what
+ << std::endl;
+ ec = 10;
+ }
return ec;
}
diff --git a/tester/covoar/TraceList.cc b/tester/covoar/TraceList.cc
index a4e29e6..51ecfd8 100644
--- a/tester/covoar/TraceList.cc
+++ b/tester/covoar/TraceList.cc
@@ -1,5 +1,6 @@
#include "TraceList.h"
-#include <stdio.h>
+#include <iostream>
+#include <iomanip>
namespace Trace {
@@ -20,7 +21,7 @@ namespace Trace {
traceRange_t t;
t.lowAddress = lowAddressArg;
- t.length = highAddressArg - lowAddressArg;
+ t.length = highAddressArg - lowAddressArg;
t.exitReason = why;
set.push_back( t );
@@ -28,12 +29,9 @@ namespace Trace {
void TraceList::ShowTrace( traceRange_t *t)
{
- printf(
- "Start 0x%x, length 0x%03x Reason %d\n",
- t->lowAddress,
- t->length,
- t->exitReason
- );
+ std::cout << std::hex << "Start 0x" << t->lowAddress
+ << ", length 0x" << std::setfill( '0' ) << std::setw( 3 )
+ << t->length << " Reason " << t->exitReason << std::endl;
}
void TraceList::ShowList()
diff --git a/tester/rt/config.py b/tester/rt/config.py
index a7b9ee3..2361bc9 100644
--- a/tester/rt/config.py
+++ b/tester/rt/config.py
@@ -37,6 +37,7 @@ from __future__ import print_function
import datetime
import os
import re
+import shlex
import threading
from rtemstoolkit import configuration
@@ -258,6 +259,7 @@ class file(config.file):
script = self.expand('%%{%s}' % data[2])
if script:
script = [l.strip() for l in script.splitlines()]
+ self.kill_on_end = True
if not self.in_error:
if self.console:
self.console.open()
@@ -282,7 +284,12 @@ class file(config.file):
raise error.general('invalid %tftp port')
self.kill_on_end = True
if not self.opts.dry_run():
+ if self.defined('session_timeout'):
+ session_timeout = int(self.expand('%{session_timeout}'))
+ else:
+ session_timeout = 120
self.process = tester.rt.tftp.tftp(bsp_arch, bsp,
+ session_timeout = session_timeout,
trace = self.exe_trace('tftp'))
if not self.in_error:
if self.console:
@@ -325,7 +332,7 @@ class file(config.file):
if len(_data):
ds = [_data[0]]
if len(_data) > 1:
- ds += _data[1].split()
+ ds += shlex.split(_data[1])
ds = self.expand(ds)
if _directive == '%console':
@@ -413,28 +420,41 @@ class file(config.file):
reset_target = True
else:
reset_target = False
- if self.target_start_regx is not None:
- if self.target_start_regx.match(text):
- if self.test_started:
- self._capture_console('target start detected')
+ if ('*** TIMEOUT TIMEOUT' in text or \
+ '*** TEST TOO LONG' in text) and \
+ self.defined('target_reset_on_timeout'):
+ reset_target = True
+ restart = \
+ (self.target_start_regx is not None and self.target_start_regx.match(text))
+ if restart:
+ if self.test_started:
+ self._capture_console('target start detected')
+ ok_to_kill = True
+ else:
+ self.restarts += 1
+ if self.restarts > self.max_restarts:
+ self._capture_console('target restart maximum count reached')
ok_to_kill = True
else:
- self.restarts += 1
- if self.restarts > self.max_restarts:
- self._capture_console('target restart maximum count reached')
- ok_to_kill = True
- else:
- self.process.target_restart(self.test_started)
+ self.process.target_restart(self.test_started)
if not reset_target and self.target_reset_regx is not None:
if self.target_reset_regx.match(text):
self._capture_console('target reset condition detected')
self._target_command('reset')
self.process.target_reset(self.test_started)
if self.kill_on_end:
- if not ok_to_kill and '*** END OF TEST ' in text:
+ if not ok_to_kill and \
+ ('*** END OF TEST ' in text or \
+ '*** FATAL ***' in text or \
+ '*** TIMEOUT TIMEOUT' in text or \
+ '*** TEST TOO LONG' in text):
self._capture_console('test end: %s' % (self.test_label))
if self.test_label is not None:
- ok_to_kill = '*** END OF TEST %s ***' % (self.test_label) in text
+ ok_to_kill = \
+ '*** END OF TEST %s ***' % (self.test_label) in text or \
+ '*** FATAL ***' in text or \
+ '*** TIMEOUT TIMEOUT' in text or \
+ '*** TEST TOO LONG' in text
self.process.target_end()
text = [(self.console_prefix, l) for l in text.replace(chr(13), '').splitlines()]
if self.output is not None:
diff --git a/tester/rt/exe.py b/tester/rt/exe.py
index 626899e..8a36aca 100644
--- a/tester/rt/exe.py
+++ b/tester/rt/exe.py
@@ -116,12 +116,12 @@ class exe(object):
def _monitor(self, timeout):
output_length = self.output_length
step = 0.25
- period = timeout[0] * step
- seconds = timeout[1] * step
+ period = timeout[0] / step
+ seconds = timeout[1] / step
while self.process and period > 0 and seconds > 0:
current_length = self.output_length
if output_length != current_length:
- period = timeout[0] * step
+ period = timeout[0] / step
output_length = current_length
if seconds < step:
seconds = 0
diff --git a/tester/rt/report.py b/tester/rt/report.py
index 38ac6f9..642ae73 100644
--- a/tester/rt/report.py
+++ b/tester/rt/report.py
@@ -78,8 +78,8 @@ class report(object):
msg += 'Failed: %*d%s' % (self.total_len, self.failed, os.linesep)
msg += 'User Input: %*d%s' % (self.total_len, self.user_input, os.linesep)
msg += 'Expected Fail: %*d%s' % (self.total_len, self.expected_fail, os.linesep)
- msg += 'Indeterminate: %*d%s' % (self.total_len, self.self.indeterminate, os.linesep)
- msg += 'Benchmark: %*d%s' % (self.total_len, self.self.benchmark, os.linesep)
+ msg += 'Indeterminate: %*d%s' % (self.total_len, self.indeterminate, os.linesep)
+ msg += 'Benchmark: %*d%s' % (self.total_len, self.benchmark, os.linesep)
msg += 'Timeout: %*d%s' % (self.total_len, self.timeouts, os.linesep)
msg += 'Test too long: %*d%s' % (self.total_len, self.test_too_long, os.linesep)
msg += 'Invalid: %*d%s' % (self.total_len, self.invalids, os.linesep)
@@ -141,11 +141,11 @@ class report(object):
if line[0] == output_prefix:
if line[1].startswith('*** '):
banner = line[1][4:]
- if banner.startswith('BEGIN OF '):
+ if banner.startswith('BEGIN OF TEST '):
start = True
- elif line[1][4:].startswith('END OF '):
+ elif banner.startswith('END OF TEST '):
end = True
- elif line[1][4:].startswith('FATAL'):
+ elif banner.startswith('FATAL'):
fatal = True
elif banner.startswith('TIMEOUT TIMEOUT'):
timeout = True
@@ -327,7 +327,7 @@ class report(object):
for name in results:
if results[name]['result'] == state:
l += [' %s' % (path.basename(name))]
- return l
+ return sorted(l)
l = []
if self.failed:
l += ['Failures:']
diff --git a/tester/rt/stty.py b/tester/rt/stty.py
index 130318d..fca026a 100644
--- a/tester/rt/stty.py
+++ b/tester/rt/stty.py
@@ -558,7 +558,7 @@ class tty(object):
self._update()
def read(self):
- return self.fs.read()
+ return self.fd.read()
if __name__ == "__main__":
if len(sys.argv) == 2:
diff --git a/tester/rt/test.py b/tester/rt/test.py
index 113936c..db5939b 100644
--- a/tester/rt/test.py
+++ b/tester/rt/test.py
@@ -151,6 +151,7 @@ class test_run(object):
name = 'test[%s]' % path.basename(self.executable)
self.thread = threading.Thread(target = self.runner,
name = name)
+ self.thread.daemon = True
self.thread.start()
def is_alive(self):
@@ -218,76 +219,48 @@ def killall(tests):
test.kill()
+def results_to_data(args, reports, start_time, end_time):
+ data = {}
+ data['report-version'] = 1
+ data['tester-version'] = version.string()
+ data['command-line'] = args
+ data['host'] = host.label(mode='all')
+ data['python'] = sys.version.replace('\n', '')
+ data['start-time'] = start_time.isoformat()
+ data['end-time'] = end_time.isoformat()
+ reports_data = []
+
+ for name, run in reports.results.items():
+ run_data = {}
+ result = run['result']
+ run_data['result'] = result
+ output = []
+ for line in run['output']:
+ if line.startswith('] '):
+ output.append(line[2:])
+ elif line.startswith('=> exe:'):
+ run_data['command-line'] = line[9:].split()
+ run_data['output'] = output
+ run_data['executable'] = name
+ run_data['executable-sha512'] = get_hash512(name)
+ run_data['start-time'] = run['start'].isoformat()
+ run_data['end-time'] = run['end'].isoformat()
+ run_data['bsp'] = run['bsp']
+ run_data['arch'] = run['bsp_arch']
+ reports_data.append(run_data)
+
+ data['reports'] = sorted(reports_data, key=lambda x: x["executable"])
+ return data
+
+
def generate_json_report(args, reports, start_time, end_time,
- total, json_file):
+ _total, json_file):
import json
- import sys
- json_log = {}
- json_log['Command Line'] = " ".join(args)
- json_log['Python'] = sys.version.replace('\n', '')
- json_log['test_groups'] = []
- json_log['Host'] = host.label(mode='all')
- json_log['summary'] = {}
- json_log['summary']['passed_count'] = reports.passed
- json_log['summary']['failed_count'] = reports.failed
- json_log['summary']['user-input_count'] = reports.user_input
- json_log['summary']['expected-fail_count'] = reports.expected_fail
- json_log['summary']['indeterminate_count'] = reports.indeterminate
- json_log['summary']['benchmark_count'] = reports.benchmark
- json_log['summary']['timeout_count'] = reports.timeouts
- json_log['summary']['test-too-long_count'] = reports.test_too_long
- json_log['summary']['invalid_count'] = reports.invalids
- json_log['summary']['wrong-version_count'] = reports.wrong_version
- json_log['summary']['wrong-build_count'] = reports.wrong_build
- json_log['summary']['wrong-tools_count'] = reports.wrong_tools
- json_log['summary']['invalid-header_count'] = reports.wrong_header
- json_log['summary']['total_count'] = reports.total
- time_delta = end_time - start_time
- json_log['summary']['average_test_time'] = str(time_delta / total)
- json_log['summary']['testing_time'] = str(time_delta)
-
- result_types = [
- 'failed', 'user-input', 'expected-fail', 'indeterminate',
- 'benchmark', 'timeout', 'test-too-long', 'invalid', 'wrong-version',
- 'wrong-build', 'wrong-tools', 'wrong-header'
- ]
- json_results = {}
- for result_type in result_types:
- json_log['summary'][result_type] = []
-
- # collate results for JSON log
- for name in reports.results:
- result_type = reports.results[name]['result']
- # map fatal-error on to failed since the report adds both to the failed count
- if result_type == "fatal-error":
- result_type = "failed"
- test_parts = name.split("/")
- test_category = test_parts[-2]
- test_name = test_parts[-1]
- if result_type != 'passed':
- json_log['summary'][result_type].append(test_name)
- if test_category not in json_results:
- json_results[test_category] = []
- json_result = {}
- # remove the file extension
- json_result["name"] = test_name.split('.')[0]
- json_result["result"] = result_type
- if result_type == "failed" or result_type == "timeout":
- json_result["output"] = reports.results[name]["output"]
- json_results[test_category].append(json_result)
-
- # convert results to a better format for report generation
- sorted_keys = sorted(json_results.keys())
- for i in range(len(sorted_keys)):
- results_log = {}
- results_log["index"] = i + 1
- results_log["name"] = sorted_keys[i]
- results_log["results"] = json_results[sorted_keys[i]]
- json_log["test_groups"].append(results_log)
-
- # write out JSON log
+
+ data = results_to_data(args, reports, start_time, end_time);
+
with open(json_file, 'w') as outfile:
- json.dump(json_log, outfile, sort_keys=True, indent=4)
+ json.dump(data, outfile, sort_keys=True, indent=4)
def generate_junit_report(args, reports, start_time, end_time,
@@ -343,108 +316,28 @@ def generate_junit_report(args, reports, start_time, end_time,
def generate_yaml_report(args, reports, start_time, end_time,
- total, yaml_file):
+ _total, yaml_file):
""" Generates a YAML file containing information about the test run,
including all test outputs """
import yaml
- def format_output(output_list):
- return "\n".join(output_list).replace("] ", '').replace('=> ', '')
-
- yaml_log = {}
- yaml_log['command-line'] = args
- yaml_log['host'] = host.label(mode='all')
- yaml_log['python'] = sys.version.replace('\n', '')
- yaml_log['summary'] = {}
- yaml_log['summary']['passed-count'] = reports.passed
- yaml_log['summary']['failed-count'] = reports.failed
- yaml_log['summary']['user-input-count'] = reports.user_input
- yaml_log['summary']['expected-fail-count'] = reports.expected_fail
- yaml_log['summary']['indeterminate-count'] = reports.indeterminate
- yaml_log['summary']['benchmark-count'] = reports.benchmark
- yaml_log['summary']['timeout-count'] = reports.timeouts
- yaml_log['summary']['test-too-long_count'] = reports.test_too_long
- yaml_log['summary']['invalid-count'] = reports.invalids
- yaml_log['summary']['wrong-version-count'] = reports.wrong_version
- yaml_log['summary']['wrong-build-count'] = reports.wrong_build
- yaml_log['summary']['wrong-tools-count'] = reports.wrong_tools
- yaml_log['summary']['wrong-header-count'] = reports.wrong_header
- yaml_log['summary']['total-count'] = reports.total
- time_delta = end_time - start_time
- yaml_log['summary']['average-test-time'] = str(time_delta / total)
- yaml_log['summary']['testing-time'] = str(time_delta)
-
- result_types = [
- 'failed', 'user-input', 'expected-fail', 'indeterminate',
- 'benchmark', 'timeout', 'test-too-long', 'invalid', 'wrong-version',
- 'wrong-build', 'wrong-tools', 'wrong-header'
- ]
- for result_type in result_types:
- yaml_log['summary'][result_type] = []
-
- result_element = {}
- yaml_log['outputs'] = []
-
- # process output of each test
- for exe_name in reports.results:
- test_parts = exe_name.split("/")
- test_name = test_parts[-1]
- result_element['executable-name'] = test_name
- result_element['executable-sha512'] = get_hash512(exe_name)
- result_element['execution-start'] = reports.results[exe_name]['start'].isoformat()
- result_element['execution-end'] = reports.results[exe_name]['end'].isoformat()
- date_diff = reports.results[exe_name]['end'] - reports.results[exe_name]['start']
- result_element['execution-duration'] = str(date_diff)
- result_element['execution-result'] = reports.results[exe_name]['result']
- result_element['bsp'] = reports.results[exe_name]['bsp']
- result_element['bsp-arch'] = reports.results[exe_name]['bsp_arch']
- result_output = reports.results[exe_name]['output']
-
- dbg_output = []
- test_output = []
- idxs_output = [] # store indices of given substrings
- for elem in result_output:
- if '=> ' in elem:
- idxs_output.append(result_output.index(elem))
- if '*** END' in elem:
- idxs_output.append(result_output.index(elem))
-
- if len(idxs_output) == 3: # test executed and has result
- dbg_output = result_output[idxs_output[0]:idxs_output[1]]
- dbg_output.append("=== Executed Test ===")
- dbg_output = dbg_output + result_output[idxs_output[2]+1:len(result_output)]
- test_output = result_output[idxs_output[1]:idxs_output[2]+1]
- else:
- dbg_output = result_output
-
- result_element['debugger-output'] = format_output(dbg_output)
- result_element['console-output'] = format_output(test_output)
- yaml_log['outputs'].append(result_element)
-
- result_type = reports.results[exe_name]['result']
- # map "fatal-error" on to "failed"
- if result_type == "fatal-error":
- result_type = "failed"
-
- if result_type != 'passed':
- yaml_log['summary'][result_type].append(test_name)
-
- result_element = {}
+ data = results_to_data(args, reports, start_time, end_time);
with open(yaml_file, 'w') as outfile:
- yaml.dump(yaml_log, outfile, default_flow_style=False, allow_unicode=True)
+ yaml.dump(data, outfile, default_flow_style=False, allow_unicode=True)
def get_hash512(exe):
""" returns SHA512 hash string of a given binary file passed as argument """
+ import base64
import hashlib
hash = hashlib.sha512()
with open(exe, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
hash.update(byte_block)
- return hash.hexdigest()
+ return base64.urlsafe_b64encode(hash.digest()).decode("ascii")
report_formatters = {
diff --git a/tester/rt/tftp.py b/tester/rt/tftp.py
index 5a1c7b7..5d2e74a 100644
--- a/tester/rt/tftp.py
+++ b/tester/rt/tftp.py
@@ -49,7 +49,8 @@ import tester.rt.tftpserver
class tftp(object):
'''RTEMS Testing TFTP base.'''
- def __init__(self, bsp_arch, bsp, trace = False):
+ def __init__(self, bsp_arch, bsp, session_timeout, trace = False):
+ self.session_timeout = session_timeout
self.trace = trace
self.lock_trace = False
self.lock = threading.RLock()
@@ -60,7 +61,7 @@ class tftp(object):
def __del__(self):
self.kill()
- def _init(self):
+ def _init(self, state = 'reset'):
self.output_length = None
self.console = None
self.server = None
@@ -73,7 +74,7 @@ class tftp(object):
self.running = False
self.finished = False
self.caught = None
- self.target_state = 'reset'
+ self.target_state = state
def _lock(self, msg):
if self.lock_trace:
@@ -109,7 +110,8 @@ class tftp(object):
try:
if self.server is not None:
self.server.stop()
- self.finished = Finished
+ self.finished = finished
+ self._set_target_state('finished')
except:
pass
@@ -150,14 +152,15 @@ class tftp(object):
def _listener(self, exe):
self.server = tester.rt.tftpserver.tftp_server(host = 'all',
port = self.port,
+ session_timeout = self.session_timeout,
timeout = 10,
forced_file = exe,
sessions = 1)
try:
- if log.tracing:
+ if False and log.tracing:
self.server.trace_packets()
self.server.start()
- self.server.run()
+ return self.server.run() == 1
except:
self.server.stop()
raise
@@ -169,22 +172,30 @@ class tftp(object):
self.exe = None
self._unlock('_runner')
caught = None
+ retry = 0
+ target_loaded = False
try:
self._lock('_runner')
state = self.target_state
self._unlock('_runner')
self._trace('runner: ' + state)
- while state not in ['shutdown', 'finished']:
- if state != 'running':
+ while state not in ['shutdown', 'finished', 'timeout']:
+ if state in ['booting', 'running']:
+ time.sleep(0.25)
+ else:
self._trace('listening: begin: ' + state)
- self._listener(exe)
+ target_loaded = self._listener(exe)
self._lock('_runner')
- if self.target_state == 'booting':
- self._set_target_state('loaded')
+ if target_loaded:
+ self._set_target_state('booting')
+ else:
+ retry += 1
+ if retry > 1:
+ self._set_target_state('timeout')
+ self._timeout()
+ state = self.target_state
self._unlock('_runner')
self._trace('listening: end: ' + state)
- else:
- time.sleep(0.25)
self._lock('_runner')
state = self.target_state
self._unlock('_runner')
@@ -214,16 +225,17 @@ class tftp(object):
self.test_too_long = timeout[3]
self.opened = True
self.running = True
+ self._console('tftp: exe: %s' % (executable))
self.listener = threading.Thread(target = self._runner,
name = 'tftp-listener')
+ self.listener.daemon = True
self._unlock('_open: start listner')
- self._console('tftp: exe: %s' % (executable))
self.listener.start()
self._lock('_open: start listner')
step = 0.25
period = timeout[0]
seconds = timeout[1]
- output_len = self.output_length()
+ output_length = self.output_length()
while not self.finished and period > 0 and seconds > 0:
if not self.running and self.caught:
break
@@ -250,7 +262,7 @@ class tftp(object):
elif seconds == 0:
self._test_too_long()
caught = self.caught
- self._init()
+ self._init('finished')
self._unlock('_open')
if caught is not None:
reraise.reraise(*caught)
diff --git a/tester/rt/tftpserver.py b/tester/rt/tftpserver.py
index 92cd1fd..c200dad 100644
--- a/tester/rt/tftpserver.py
+++ b/tester/rt/tftpserver.py
@@ -453,14 +453,13 @@ class udp_handler(socketserver.BaseRequestHandler):
raise
self._notice('] tftp: %d: error: %s: %s' % (index, type(exp), exp))
self._notice('] tftp: %d: end: %s' % (index, client))
+ self.server.tftp.session_done()
def handle(self):
'''The UDP server handle method.'''
- if self.server.tftp.sessions is None \
- or self.server.tftp.session < self.server.tftp.sessions:
+ if self.server.tftp.sessions_available():
self.handle_session(self.server.tftp.next_session())
-
class udp_server(socketserver.ThreadingMixIn, socketserver.UDPServer):
'''UDP server. Default behaviour.'''
@@ -474,6 +473,7 @@ class tftp_server(object):
def __init__(self,
host,
port,
+ session_timeout=None,
timeout=10,
base=None,
forced_file=None,
@@ -484,6 +484,7 @@ class tftp_server(object):
self.notices = False
self.packet_trace = False
self.exception_is_raise = False
+ self.session_timeout = session_timeout
self.timeout = timeout
self.host = host
self.port = port
@@ -497,6 +498,7 @@ class tftp_server(object):
raise error.general('tftp session count is not a number')
self.sessions = sessions
self.session = 0
+ self.sessions_done = 0
self.reader = reader
def __del__(self):
@@ -542,6 +544,8 @@ class tftp_server(object):
def run(self):
'''Run the TFTP server for the specified number of sessions.'''
running = True
+ session_timeout = self.session_timeout
+ last_session = 0
while running:
period = 1
self._lock()
@@ -549,7 +553,7 @@ class tftp_server(object):
running = False
period = 0
elif self.sessions is not None:
- if self.sessions == 0:
+ if self.sessions_done >= self.sessions:
running = False
period = 0
else:
@@ -557,7 +561,24 @@ class tftp_server(object):
self._unlock()
if period > 0:
time.sleep(period)
+ if session_timeout is not None:
+ session = self.get_session()
+ if last_session != session:
+ last_session = session
+ session_timeout = self.session_timeout
+ else:
+ if session_timeout < period:
+ session_timeout = 0
+ else:
+ session_timeout -= period
+ if session_timeout == 0:
+ log.trace('] tftp: server: session timeout')
+ running = False
self.stop()
+ self._lock()
+ sessions_done = self.sessions_done
+ self._unlock()
+ return sessions_done
def get_session(self):
'''Return the session count.'''
@@ -580,6 +601,24 @@ class tftp_server(object):
self._unlock()
return count
+ def sessions_available(self):
+ '''Return True is there are available sessions.'''
+ available = False
+ self._lock()
+ try:
+ available = self.sessions is None or self.session < self.sessions
+ finally:
+ self._unlock()
+ return available
+
+ def session_done(self):
+ '''Call when a session is done.'''
+ self._lock()
+ try:
+ self.sessions_done += 1
+ finally:
+ self._unlock()
+
def enable_notices(self):
'''Call to enable notices. The server is quiet without this call.'''
self._lock()
@@ -654,10 +693,14 @@ def run(args=sys.argv, command_path=None):
help='port to bind the server too (default: %(default)s).',
type=int,
default='69')
+ argsp.add_argument('-S', '--session-timeout',
+ help='timeout in seconds, client can override ' \
+ '(default: %(default)s).',
+ type = int, default=None)
argsp.add_argument('-t', '--timeout',
- help = 'timeout in seconds, client can override ' \
+ help='timeout in seconds, client can override ' \
'(default: %(default)s).',
- type = int, default = '10')
+ type=int, default='10')
argsp.add_argument(
'-b',
'--base',
@@ -682,7 +725,7 @@ def run(args=sys.argv, command_path=None):
log.output(log.info(args))
log.tracing = argopts.trace
- server = tftp_server(argopts.bind, argopts.port, argopts.timeout,
+ server = tftp_server(argopts.bind, argopts.port, argopts.session_timeout, argopts.timeout,
argopts.base, argopts.force_file,
argopts.sessions)
server.enable_notices()
diff --git a/tester/rtems/testing/bsps/erc32-sis.ini b/tester/rtems/testing/bsps/erc32-sis.ini
index fca2122..a025265 100644
--- a/tester/rtems/testing/bsps/erc32-sis.ini
+++ b/tester/rtems/testing/bsps/erc32-sis.ini
@@ -34,6 +34,5 @@
[erc32-sis]
bsp = erc32
arch = sparc
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-sis
-bsp_run_opts = -nouartrx -r -tlim 600 s
+tester = %{_rtscripts}/sis.cfg
+bsp_run_opts =
diff --git a/tester/rtems/testing/bsps/gr740-sis.ini b/tester/rtems/testing/bsps/gr740-sis.ini
index b71048c..c42d716 100644
--- a/tester/rtems/testing/bsps/gr740-sis.ini
+++ b/tester/rtems/testing/bsps/gr740-sis.ini
@@ -33,6 +33,5 @@
[gr740-sis]
bsp = gr740
arch = sparc
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-sis
-bsp_run_opts = -gr740 -nouartrx -r -tlim 200 s -m 4
+tester = %{_rtscripts}/sis.cfg
+bsp_run_opts = -gr740 -m 4
diff --git a/tester/rtems/testing/bsps/griscv-sis-cov.ini b/tester/rtems/testing/bsps/griscv-sis-cov.ini
index 9ab37a8..fa86b55 100644
--- a/tester/rtems/testing/bsps/griscv-sis-cov.ini
+++ b/tester/rtems/testing/bsps/griscv-sis-cov.ini
@@ -34,7 +34,6 @@
[griscv-sis-cov]
bsp = griscv-sis
arch = riscv
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-sis
-bsp_run_opts = -nouartrx -r -tlim 300 s -m 4 -cov
+tester = %{_rtscripts}/sis.cfg
+bsp_run_opts = -m 4 -cov
bsp_covoar_cmd = -S %{bsp_symbol_path} -E %{cov_explanations} -f TSIM
diff --git a/tester/rtems/testing/bsps/griscv-sis.ini b/tester/rtems/testing/bsps/griscv-sis.ini
index b21cba1..bf32851 100644
--- a/tester/rtems/testing/bsps/griscv-sis.ini
+++ b/tester/rtems/testing/bsps/griscv-sis.ini
@@ -34,6 +34,5 @@
[griscv-sis]
bsp = griscv
arch = riscv
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-sis
-bsp_run_opts = -nouartrx -r -tlim 300 s -m 4
+tester = %{_rtscripts}/sis.cfg
+bsp_run_opts = -m 4
diff --git a/tester/rtems/testing/bsps/imx7.ini b/tester/rtems/testing/bsps/imx7.ini
index 0a56ba8..c16bc6c 100644
--- a/tester/rtems/testing/bsps/imx7.ini
+++ b/tester/rtems/testing/bsps/imx7.ini
@@ -38,5 +38,5 @@ arch = arm
jobs = 1
tester = %{_rtscripts}/tftp.cfg
test_restarts = 3
-target_start_regex = Hit any key to stop autoboot:
+target_start_regex = .*U-Boot .*
requires = bsp_tty_dev
diff --git a/tester/rtems/testing/bsps/leon2-sis.ini b/tester/rtems/testing/bsps/leon2-sis.ini
index 61205ad..810320c 100644
--- a/tester/rtems/testing/bsps/leon2-sis.ini
+++ b/tester/rtems/testing/bsps/leon2-sis.ini
@@ -34,6 +34,5 @@
[leon2-sis]
bsp = leon2
arch = sparc
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-sis
-bsp_run_opts = -leon2 -nouartrx -r -tlim 200 s
+tester = %{_rtscripts}/sis.cfg
+bsp_run_opts = -leon2
diff --git a/tester/rtems/testing/bsps/leon3-run.ini b/tester/rtems/testing/bsps/leon3-run.ini
index a8c97a6..99c391b 100644
--- a/tester/rtems/testing/bsps/leon3-run.ini
+++ b/tester/rtems/testing/bsps/leon3-run.ini
@@ -34,6 +34,5 @@
[leon3-run]
bsp = leon3
arch = sparc
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-run
+tester = %{_rtscripts}/sis.cfg
bsp_run_opts = -a -leon3
diff --git a/tester/rtems/testing/bsps/leon3-sis-cov.ini b/tester/rtems/testing/bsps/leon3-sis-cov.ini
index d8ffe28..7c6a279 100644
--- a/tester/rtems/testing/bsps/leon3-sis-cov.ini
+++ b/tester/rtems/testing/bsps/leon3-sis-cov.ini
@@ -34,7 +34,6 @@
[leon3-sis-cov]
bsp = leon3-sis
arch = sparc
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-sis
-bsp_run_opts = -leon3 -nouartrx -r -tlim 200 s -cov
+tester = %{_rtscripts}/sis.cfg
+bsp_run_opts = -leon3 -cov
bsp_covoar_cmd = -S %{bsp_symbol_path} -E %{cov_explanations} -f TSIM
diff --git a/tester/rtems/testing/bsps/leon3-sis.ini b/tester/rtems/testing/bsps/leon3-sis.ini
index 2f933a7..9035f48 100644
--- a/tester/rtems/testing/bsps/leon3-sis.ini
+++ b/tester/rtems/testing/bsps/leon3-sis.ini
@@ -34,6 +34,5 @@
[leon3-sis]
bsp = leon3
arch = sparc
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-sis
-bsp_run_opts = -leon3 -nouartrx -r -tlim 200 s -m 4
+tester = %{_rtscripts}/sis.cfg
+bsp_run_opts = -leon3 -m 4
diff --git a/tester/rtems/testing/bsps/qoriq_e500.ini b/tester/rtems/testing/bsps/qoriq_e500.ini
index 322069b..995b6e8 100644
--- a/tester/rtems/testing/bsps/qoriq_e500.ini
+++ b/tester/rtems/testing/bsps/qoriq_e500.ini
@@ -38,5 +38,5 @@ arch = powerpc
jobs = 1
tester = %{_rtscripts}/tftp.cfg
test_restarts = 3
-target_start_regex = U-Boot
+target_start_regex = .*U-Boot .*
requires = bsp_tty_dev
diff --git a/tester/rtems/testing/bsps/qoriq_e6500_32.ini b/tester/rtems/testing/bsps/qoriq_e6500_32.ini
index 26ee3ae..37feac4 100644
--- a/tester/rtems/testing/bsps/qoriq_e6500_32.ini
+++ b/tester/rtems/testing/bsps/qoriq_e6500_32.ini
@@ -39,5 +39,5 @@ arch = powerpc
jobs = 1
tester = %{_rtscripts}/tftp.cfg
test_restarts = 3
-target_start_regex = U-Boot
+target_start_regex = .*U-Boot .*
requires = bsp_tty_dev
diff --git a/tester/rtems/testing/bsps/qoriq_e6500_64.ini b/tester/rtems/testing/bsps/qoriq_e6500_64.ini
index c9b6ab7..6e2e0b2 100644
--- a/tester/rtems/testing/bsps/qoriq_e6500_64.ini
+++ b/tester/rtems/testing/bsps/qoriq_e6500_64.ini
@@ -39,5 +39,5 @@ arch = powerpc
jobs = 1
tester = %{_rtscripts}/tftp.cfg
test_restarts = 3
-target_start_regex = U-Boot
+target_start_regex = .*U-Boot .*
requires = bsp_tty_dev
diff --git a/tester/rtems/testing/bsps/rv64imac_medany_spike.ini b/tester/rtems/testing/bsps/rv32i.ini
index 1d9a522..1817216 100644
--- a/tester/rtems/testing/bsps/rv64imac_medany_spike.ini
+++ b/tester/rtems/testing/bsps/rv32i.ini
@@ -1,6 +1,7 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2010-2014 Chris Johns (chrisj@rtems.org)
+# Copyright 2020 Hesham Almatary
+# Copyright 2018 embedded brains GmbH
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -28,11 +29,9 @@
# POSSIBILITY OF SUCH DAMAGE.
#
-#
-# The Generic RISC-V BSP
-#
-[rv64imac_medany_spike]
-bsp = rv64imac
-arch = riscv64
-tester = %{_rtscripts}/spike.cfg
-bsp_spike_opts = --isa=RV64IMAC
+[rv32i]
+bsp = rv32i
+arch = riscv32
+tester = %{_rtscripts}/qemu.cfg
+bsp_qemu_image_type = -bios
+bsp_qemu_opts = -no-reboot -nographic %{qemu_opts_no_net} -machine virt -m 128M
diff --git a/tester/rtems/testing/bsps/rv64imafd_medany_spike.ini b/tester/rtems/testing/bsps/rv32imafdc.ini
index 9348131..2f91a9a 100644
--- a/tester/rtems/testing/bsps/rv64imafd_medany_spike.ini
+++ b/tester/rtems/testing/bsps/rv32imafdc.ini
@@ -1,6 +1,7 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2010-2014 Chris Johns (chrisj@rtems.org)
+# Copyright 2020 Hesham Almatary
+# Copyright 2018 embedded brains GmbH
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -28,11 +29,9 @@
# POSSIBILITY OF SUCH DAMAGE.
#
-#
-# The Generic RISC-V BSP
-#
-[rv64imafd_medany_spike]
-bsp = rv64imafd
-arch = riscv64
-tester = %{_rtscripts}/spike.cfg
-bsp_spike_opts = --isa=RV64IMAFD
+[rv32imafdc]
+bsp = rv32imafdc
+arch = riscv32
+tester = %{_rtscripts}/qemu.cfg
+bsp_qemu_image_type = -bios
+bsp_qemu_opts = -no-reboot -nographic %{qemu_opts_no_net} -machine virt -m 128M
diff --git a/tester/rtems/testing/bsps/rv64imac_spike.ini b/tester/rtems/testing/bsps/rv64imac_spike.ini
index b2a35fd..10900d8 100644
--- a/tester/rtems/testing/bsps/rv64imac_spike.ini
+++ b/tester/rtems/testing/bsps/rv64imac_spike.ini
@@ -35,4 +35,4 @@
bsp = rv64imac
arch = riscv64
tester = %{_rtscripts}/spike.cfg
-bsp_spike_opts = --isa=RV64IMAC -m0x70000000:0x10000000
+bsp_spike_opts = --isa=RV64IMAC
diff --git a/tester/rtems/testing/bsps/rv64imafd_medany.ini b/tester/rtems/testing/bsps/rv64imafd.ini
index 15137df..bbb4ee3 100644
--- a/tester/rtems/testing/bsps/rv64imafd_medany.ini
+++ b/tester/rtems/testing/bsps/rv64imafd.ini
@@ -28,8 +28,9 @@
# POSSIBILITY OF SUCH DAMAGE.
#
-[rv64imafd_medany]
-bsp = rv64imafd_medany
+[rv64imafd]
+bsp = rv64imafd
arch = riscv64
tester = %{_rtscripts}/qemu.cfg
-bsp_qemu_opts = -no-reboot -nographic %{qemu_opts_no_net} -machine virt -m 64M
+bsp_qemu_image_type = -bios
+bsp_qemu_opts = -no-reboot -nographic %{qemu_opts_no_net} -machine virt -m 128M
diff --git a/tester/rtems/testing/bsps/rv64imafd_spike.ini b/tester/rtems/testing/bsps/rv64imafd_spike.ini
index 74336aa..8e975f7 100644
--- a/tester/rtems/testing/bsps/rv64imafd_spike.ini
+++ b/tester/rtems/testing/bsps/rv64imafd_spike.ini
@@ -35,4 +35,4 @@
bsp = rv64imafd
arch = riscv64
tester = %{_rtscripts}/spike.cfg
-bsp_spike_opts = --isa=RV64IMAFD -m0x70000000:0x10000000
+bsp_spike_opts = --isa=RV64IMAFD
diff --git a/tester/rtems/testing/bsps/rv64imafdc_medany.ini b/tester/rtems/testing/bsps/rv64imafdc.ini
index 30abd50..d42680b 100644
--- a/tester/rtems/testing/bsps/rv64imafdc_medany.ini
+++ b/tester/rtems/testing/bsps/rv64imafdc.ini
@@ -29,8 +29,9 @@
# POSSIBILITY OF SUCH DAMAGE.
#
-[rv64imafdc_medany]
-bsp = rv64imafdc_medany
+[rv64imafdc]
+bsp = rv64imafdc
arch = riscv64
tester = %{_rtscripts}/qemu.cfg
-bsp_qemu_opts = -no-reboot -nographic %{qemu_opts_no_net} -machine virt -m 64M
+bsp_qemu_image_type = -bios
+bsp_qemu_opts = -no-reboot -nographic %{qemu_opts_no_net} -machine virt -m 128M
diff --git a/tester/rtems/testing/bsps/rv64imafdc_spike.ini b/tester/rtems/testing/bsps/rv64imafdc_spike.ini
index b72b471..c95a840 100644
--- a/tester/rtems/testing/bsps/rv64imafdc_spike.ini
+++ b/tester/rtems/testing/bsps/rv64imafdc_spike.ini
@@ -35,4 +35,4 @@
bsp = rv64imafdc
arch = riscv64
tester = %{_rtscripts}/spike.cfg
-bsp_spike_opts = --isa=RV64IMAFDC -m0x70000000:0x10000000
+bsp_spike_opts = --isa=RV64IMAFDC
diff --git a/tester/rtems/testing/bsps/sis-run.ini b/tester/rtems/testing/bsps/sis-run.ini
index 55c30ac..4861231 100644
--- a/tester/rtems/testing/bsps/sis-run.ini
+++ b/tester/rtems/testing/bsps/sis-run.ini
@@ -34,6 +34,5 @@
[sis-run]
bsp = sis
arch = sparc
-tester = %{_rtscripts}/run.cfg
-bsp_run_cmd = %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-run
-bsp_run_opts = -a -nouartrx
+tester = %{_rtscripts}/sis.cfg
+bsp_run_opts =
diff --git a/tester/rtems/testing/bsps/sis.ini b/tester/rtems/testing/bsps/sis.ini
index b5d700f..086cb45 100644
--- a/tester/rtems/testing/bsps/sis.ini
+++ b/tester/rtems/testing/bsps/sis.ini
@@ -34,8 +34,6 @@
[sis]
bsp = sis
arch = sparc
-tester = %{_rtscripts}/gdb.cfg
+tester = %{_rtscripts}/sis.cfg
gdb_script = bsp_gdb_script
-bsp_gdb_script = target sim -a -nouartrx
- load
- run
+bsp_run_opts =
diff --git a/tester/rtems/testing/bsps/rv64imafdc_medany_spike.ini b/tester/rtems/testing/bsps/stm32h7-stlink.ini
index b691a60..2c375f5 100644
--- a/tester/rtems/testing/bsps/rv64imafdc_medany_spike.ini
+++ b/tester/rtems/testing/bsps/stm32h7-stlink.ini
@@ -1,6 +1,6 @@
#
# RTEMS Tools Project (http://www.rtems.org/)
-# Copyright 2010-2014 Chris Johns (chrisj@rtems.org)
+# Copyright 2015 On-Line Applications Research Corporation (OAR).
# All rights reserved.
#
# This file is part of the RTEMS Tools package in 'rtems-tools'.
@@ -29,10 +29,16 @@
#
#
-# The Generic RISC-V BSP
-#
-[rv64imafdc_medany_spike]
-bsp = rv64imafdc_medany
-arch = riscv64
-tester = %{_rtscripts}/spike.cfg
-bsp_spike_opts = --isa=RV64IMAFDC
+# The stm32h7 BSP family
+#
+[stm32h7-stlink]
+bsp = stm32h7-stlink
+arch = arm
+tester = %{_rtscripts}/gdb.cfg
+jobs = 1
+gdb_script = bsp_gdb_script
+requires = bsp_tty_dev, bsp_gdb_script, target_pretest_command, target_posttest_command
+bsp_gdb_script = target extended-remote :61234
+ load
+ handle SIGTRAP nostop
+ cont
diff --git a/tester/rtems/testing/qemu.cfg b/tester/rtems/testing/qemu.cfg
index 3c51bee..0b592ef 100644
--- a/tester/rtems/testing/qemu.cfg
+++ b/tester/rtems/testing/qemu.cfg
@@ -78,6 +78,9 @@
%ifn %{defined bsp_qemu_extra_opts}
%define bsp_qemu_extra_opts %{nil}
%endif
+%ifn %{defined bsp_qemu_image_type}
+ %define bsp_qemu_image_type -kernel
+%endif
%define qemu_cmd qemu-system-%{bsp_arch}
%define qemu_opts %{bsp_qemu_opts} %{bsp_qemu_cov_opts}
@@ -85,4 +88,4 @@
#
# Executable
#
-%execute %{qemu_cmd} %{qemu_opts} %{bsp_qemu_extra_opts} -kernel %{test_executable}
+%execute %{qemu_cmd} %{qemu_opts} %{bsp_qemu_extra_opts} %{bsp_qemu_image_type} %{test_executable}
diff --git a/tester/rtems/testing/sis.cfg b/tester/rtems/testing/sis.cfg
new file mode 100644
index 0000000..bb8d0bc
--- /dev/null
+++ b/tester/rtems/testing/sis.cfg
@@ -0,0 +1,72 @@
+#
+# RTEMS Tools Project (http://www.rtems.org/)
+# Copyright 2022 Chris Johns (chrisj@rtems.org)
+# All rights reserved.
+#
+# This file is part of the RTEMS Tools package in 'rtems-tools'.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+#
+# SIS. The main simulator for SPARC/LEON
+#
+# Use sis command to run the executable in the spike simulator.
+#
+
+%include %{_configdir}/base.cfg
+%include %{_configdir}/checks.cfg
+
+#
+# Console.
+#
+%define console_stdio
+%include %{_configdir}/console.cfg
+
+#
+# RTEMS version
+#
+%include %{_rtdir}/rtems/version.cfg
+
+#
+# Timeout option. This is the default for timeout for the CPU realtime
+# clock
+
+%ifn %{defined sis_time_limit}
+ %define sis_time_limit -tlim 400 s
+%endif
+
+#
+# Default command
+#
+%ifn %{defined bsp_run_cmd}
+ %define bsp_run_cmd %{rtems_tools}/%{bsp_arch}-rtems%{rtems_version}-sis
+%endif
+
+%define sis_cmd %{bsp_run_cmd}
+%define sis_opts -nouartrx -r %{sis_time_limit} %{bsp_run_opts}
+
+#
+# Executable
+#
+%execute %{sis_cmd} %{sis_opts} %{test_executable}
diff --git a/trace/record/record-main-lttng.cc b/trace/record/record-main-lttng.cc
index faa762d..9993565 100644
--- a/trace/record/record-main-lttng.cc
+++ b/trace/record/record-main-lttng.cc
@@ -221,6 +221,8 @@ class LTTNGClient : public Client {
void WriteIRQHandlerExit(PerCPUContext* pcpu, const ClientItem& item);
+ void ResetThreadName(PerCPUContext* pcpu, const ClientItem& item);
+
void AddThreadName(PerCPUContext* pcpu, const ClientItem& item);
void PrintItem(const ClientItem& item);
@@ -437,6 +439,20 @@ void LTTNGClient::WriteIRQHandlerExit(PerCPUContext* pcpu,
std::fwrite(&ih, sizeof(ih), 1, pcpu->event_stream);
}
+void LTTNGClient::ResetThreadName(PerCPUContext* pcpu, const ClientItem& item) {
+ pcpu->thread_id = item.data;
+ pcpu->thread_ns = item.ns;
+ pcpu->thread_name_index = 0;
+
+ uint32_t api_index = GetAPIIndexOfID(pcpu->thread_id);
+ if (api_index >= THREAD_API_COUNT) {
+ return;
+ }
+
+ uint32_t obj_index = GetObjIndexOfID(pcpu->thread_id);
+ std::memset(&thread_names_[api_index][obj_index][0], 0, THREAD_NAME_SIZE);
+}
+
void LTTNGClient::AddThreadName(PerCPUContext* pcpu, const ClientItem& item) {
if (pcpu->thread_name_index >= THREAD_NAME_SIZE) {
return;
@@ -489,10 +505,9 @@ void LTTNGClient::PrintItem(const ClientItem& item) {
WriteSchedSwitch(&pcpu, item);
}
break;
+ case RTEMS_RECORD_THREAD_CREATE:
case RTEMS_RECORD_THREAD_ID:
- pcpu.thread_id = item.data;
- pcpu.thread_ns = item.ns;
- pcpu.thread_name_index = 0;
+ ResetThreadName(&pcpu, item);
break;
case RTEMS_RECORD_INTERRUPT_ENTRY:
WriteIRQHandlerEntry(&pcpu, item);
diff --git a/trace/wscript b/trace/wscript
index a3dd5d5..b632291 100644
--- a/trace/wscript
+++ b/trace/wscript
@@ -45,7 +45,14 @@ def configure(conf):
if conf.check(header_name='zlib.h', features='cxx', mandatory=False):
conf.check_cxx(lib = 'z')
conf.check_cxx(lib = 'ws2_32', mandatory=False)
- conf.check_cxx(cxxflags='-std=c++14', mandatory=False, define_name="HAVE_STD_CXX14")
+ if conf.check_cxx(cxxflags='-std=c++20', mandatory=False, define_name="HAVE_STD_CXX20"):
+ conf.env.append_value('CXXFLAGS', '-std=c++20')
+ elif conf.check_cxx(cxxflags='-std=c++17', mandatory=False, define_name="HAVE_STD_CXX17"):
+ conf.env.append_value('CXXFLAGS', '-std=c++17')
+ elif conf.check_cxx(cxxflags='-std=c++14', mandatory=False, define_name="HAVE_STD_CXX14"):
+ conf.env.append_value('CXXFLAGS', '-std=c++14')
+ else:
+ conf.env.append_value('CXXFLAGS', '-std=c++11')
conf.write_config_header('config.h')
def build(bld):
@@ -62,9 +69,14 @@ def build(bld):
conf['optflags'] = bld.env.C_OPTS
cstd = '-std=c99'
conf['cflags'] = [cstd] + ['-pipe', '-g'] + conf['optflags']
- cxxstd = '-std=c++11'
- if bld.env.HAVE_STD_CXX14:
+ if bld.env.HAVE_STD_CXX20:
+ cxxstd = '-std=c++20'
+ elif bld.env.HAVE_STD_CXX17:
+ cxxstd = '-std=c++17'
+ elif bld.env.HAVE_STD_CXX14:
cxxstd = '-std=c++14'
+ else:
+ cxxstd = '-std=c++11'
conf['cxxflags'] = [cxxstd] + ['-pipe', '-g'] + conf['optflags']
conf['linkflags'] = ['-g']
conf['lib'] = []
@@ -101,4 +113,4 @@ def build(bld):
lib = conf['lib'])
def tags(ctx):
- ctx.exec_command('etags $(find . -name \*.[sSch])', shell = True)
+ ctx.exec_command('etags $(find . -name \\*.[sSch])', shell = True)
diff --git a/waf b/waf
index 7ceee16..3099c10 100755
--- a/waf
+++ b/waf
@@ -32,13 +32,13 @@ POSSIBILITY OF SUCH DAMAGE.
import os, sys, inspect
-VERSION="2.0.19"
-REVISION="1f3c580272b15a03d2566843c5fe872a"
-GIT="61ee22b598cf80e260beb64e475966f58b304d0d"
+VERSION="2.0.25"
+REVISION="767522112be77f8585812fcfaa08e805"
+GIT="39ef33e48380a2db38a0eae40a3a4a2c954f4450"
INSTALL=''
-C1='#6'
-C2='#.'
-C3='#%'
+C1='#+'
+C2='#*'
+C3='#)'
cwd = os.getcwd()
join = os.path.join
@@ -168,6 +168,6 @@ if __name__ == '__main__':
Scripting.waf_entry_point(cwd, VERSION, wafdir)
#==>
-#BZh91AY&SY9 \DPm(¬#%00e(b/mЀ#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%#%zmmkZ旲U#.[iھ:'kd7{o۶ֵK]v'OgvsN)#.Q+ٽpޝ]/{m<{guҋ@{vo;;vi44l{|U9ݟݚyx#%#%@(}h#%` ﲀ=wf:4[aӹyA껴4=d+֨)f0#6 *{Q$#%#6#%YJzV%}jaj3TTf˦Iv]s}6u7z:#.xW_=z֭Z;ۻ}sy{izr>^}azz{x-z̀ݳֲ4fs`uF"EE7w#%(J*OC#%l=n{Ywd >}[vG`>EmkMW`Ly^u#%}at;o}|{mOvpǼ΀o*WƘܶ2>ϟw];j9Qt׷Zt>ܮu|}ܬ&Nq\:.^yaCӶۛХ&nCz|ilͶв[ۺ5koXqؗnl/`{w>PJPTTjB7uv&iv]vݛ:kkUUܘ{x)HЃɯfw#%7ZI#%#%;^3F-mzݙ_U.us[ v]]|H,q{c;vJ{+l+Wg`;{n#]}ww{xv#.mEf"sﯼl$/gєx<V6EǑޮGxO{>w޴#%*;F7;g<uoSkݭӭm9.ݬ ݾwwwtv:#.[.wc ӂhgu^nu/=4#%cM{JU){xW`{VU[}IzovuH*u$I֪nc[נ&ƻ}ac| Gͼ&:}6;vRw}-[nh &#%& CBa0#.#%PTzК#%{SA)B @@$zzI 4#%#%#%#%#%#%#%D!M5TMoU<zT()mG#%#%ѣ@#%#%'RD!4dOD$OFM#.@#.Sj#%#%#%#%#%#%$!#%&@bh&M#%M#.4Si4 #%h#%#%#%I L#A&=&m{SIh#%d4#%#%?Uir\QWwkZvhʃ>5Zu!LA ,LQ*")PcZ5OӦ Jtđ8WoT"]^S_3os2 lDs4;mqE6M`Vd]j◘5o# UI7wDKU/~oBE"SU5k3khȋ 7#%H*-E :$H& "HP !#%h#%$@d P@mfLCLd@M$Rj63525)FSm&‰$J2ZQh[Fi,iZ!#.F)iMF #.1eMDRlZSMhYi#. (cRFQ&JlBjcI@hHRF"[MUifLM& mM65%)-53fZL1fdlB!QfR`4TH!`ؤfJb0lBXhdFIR&B4Cl &)BFRf$@YY5ccE#6dK) -) %&ED2hhɉIF($@VAMEfR` ؉1M ͂ab6V$$PRD%Q0EaIL# &RJMEXHjHb iM$"H[b2F̒3bdE(͑&U4Đ)AQ`,i"5+%6(R"I&aLcI &Qi3H("hkP,&YA2%&DM2*4f6)16A! "1F4, &T՘l(#.H!HСYM$QF&JFhb22iD`JM52i!)#.1Sb5*R)#.2)H(HR"E$M)i5DFh&TiD#.,eDYJbD!f k6lmd$4Ld5EPZ XR$cc1 2RXYJ&EM(ZhII$2ɲ#)E,ѥ15e"$4M`BjfF&RLȈ6,&%T٢-L-bC؈RD#HJm~ka͢lVƶ*6L4R44F5Yc(S*HD[%QIKQ&D5acR#6e2eI*4TB+d"TT›63,YLS,JJSLl)%2RZ!գ%AEVMdjEHkD$mhŊl2ѵ0U@iR F2&ƍ&di ժ* KY4IR1!MlY"Y1Jm*RYaZDFK"e5MMk,Xlk+,l  Db4!-T%KQY&6LJ,&l#."+,E1Jl4 AE$&Fm6F6ScE%bH#!4LQJdRP5BT4 JBj-#636)F̤!Hbki4F5&a $YP-4) Ě4Yda, jE54ECF)6bfQj4  2A&ĕ%I1Rcda)ME`0٤lfECJ2ZfhT2Hl0D2hihl,LDRBAh[ QQ-IM#6LR#b-fC26*2%,ѴkTI5E$ԚPdjkA4!E$Ih5EJ"Fd̡jQHfJ$(j#V#.2,Y*6)42V-&)+)DMTRccdd&#HRLɃA#6)6&"ƨY)&RV-ɈF42JԈieheji5 Q5%)Й̌ƚL&kDlU%hԕ,&Lڐ1lj cc$UE2X61mIQ f-F+iQQJVf*B (4X"J&,ʒ+I[M+FbbIE[lR&T*&$1P"CFi&%Xش[F542Y6!mIME26QAjMIʈBLI&RLd"-тnԥC)B51jTqe+(a(h! b LL$hlNz9SUIP?[lȟYLsȠ;L(0\:8pI d*߱r*3ZI(R+g,$b/0-npz%ZM8wBd~ ՋN؜Z,3cRR(lrrXݳfVc`I"lLhcz瓓G]޹w^\/uu\LP"W4LvcK@JAF&ēMb3(]enY74h6 m `ٗ(c2@szo(7p c#6Gv),`rR[R}dzgsNVQL0ФY;d8$ϣ؞j(%?rõ Jxkcx-2Kw%CUyyܖw^Qr車esnCQW6qllO5Z4EA?_W-ȦWȶIu?Go*y|m<b#4L9cuPEX#.t@#6A!##.)Hz^+\Ѩ8wcr˙(ˮ$s5ZAhJr.YlC,0!M te#6R79,פFtxV@Gd%l p<k9c|Ǚ#tE]H P0T[؃_#zrHȦwԊW7mxVY? )rvjoQwEme#.dԂ׶bN!e &u/K`nnT)"*#6d9 #c*w&bۆfR6z:'.k,YL#6N*}y>㊝U"֍UOpi)8+ Ӭ,j[vlQa1KFS7mZ&#6*NP-#6A@*{iX<ZW(EEEnY5͵w>S6ߋMfhF%2+zmW!bSQIܮuV(i%"@X'+&D)J墶/]^"Rkb_-׻*|{=ol|14> ݌>LȲ"4~"UrPaLֶjϹ$![z۲FlWW=4DalAHPŤ''mBCGE2R]U"wլ*Х²SE#6lQ MZH"JuJ *Ó#6ILe„S$+ bN<kH](#6<*oЗAJ /;qap^Ro^Xj"_g<TCsyXq&qK\:05aeF1PXiM3g{O4urgko3 )#i'־ʣwr8;++oHm^Lq#.1Ir%nCM}ז6qv~5.|bA#S<*Jt\D%*9<}/"#.L3v+T-,g)D!;SŐa=3DKA]( HvR߁#v/6PE#Dž8qΰK(q<>:+#%(\Hp6疴zm*'ܩ1?_k#.VFyeo$Á?jfAv߾ڜNz$!.fܡ\i̩#.#E0F:E&}ٞAg]p?X7d1\NB=}ILƘ#6P#6;g="<R:I2\q.V)gɅ,8`=jPqëZ#.,nMB88׵ۺ1il m׺|5=Z鵦Lu $(w0WX uBdN>ܸIiEw+^{5ZU{)cT @^ǡ=4 Zۍo4^1É -onU>iK|eJ/}m}w*IeQ92e@ hQAA9CrOuu5&JF}w/šjj}( +|~TGR{ES>,߻jj܇Y+$.h>nj]Pꩨ ׶{gmk4Ok{єEˣ;ndmAyQ#6ӫfͲX8TNL\P苁1 63Y yUM3#.X|TW7ΊD^jER,)9߹ȷ)ьuO~$_s4?Zu>]1ykEX)>Om8ŝOz\ڸ;R1~FS_t“Rn(PbRfWYʡcPBYׅDQQe0x~{*nf wiXT)6-;ˣw$3}T|-z85`Bhҡ~J΀Z+9rlVx#6h6Dx0)AX*0_#.ǷzeH,5Aޟ{e7S&yY񯥳LSœ?ك-y69^}pBIiK&nǨsιߎe,&2:L[xHǮ*U91 DL5;i傄QT nUّ`ҟ#oJ41Sv馫a離M4O|I$ {Ur0;0*zG/7+ #%Qb]K\.) I9qa#.5Кw*8A'<:b2xQn:,öTl8:<y9CM\"#.JvX=w%gC͕PGhaH)U.ۙA=s ȫsH]#.$8.\ӳMޛ TTaM} P ȥ>?kborR#R/*3Ѧ4uocȐ#.vnVp)rZ,.O}zwln.LQy@EHiw=Qq"%V2&0+Vqw=Pu7V^-EuQQ'U0D}ޙNi$'1ΝKU=_lS#60D1zǾ+dpڈZ?wx\`<+fJ~WKոݝ8n$zG<c/Y vfz3Lcz7k%EΛY-J`]BvP\Hm2LMwr4:Œ`\Խu}<(f`ňͽUQ9c:O+%PiP/<H`6,I<Ye"4EC@fzbFGB>qz< #?}IL_K'B#+|mתǒ7"nb#sh~?~V]6k)0 UBU,'@5+lX6ɠn.{p}:ybsf19%Z##6 ,&ў3|#6y".$#. #.u. ד*{gdevKfQο7N hÄe hP"1bL`Mm"-X]#6=)#.2¿+]tg:00YUDƊHI#L͒6\j}#.ۻ6=U Sޚt4s ٺ"g.t(:qc1^n*fbfۊ;Xh1as>=N#.(qنfT`al~28r!:Y@e jNec;8`ij%cW?DM@0L\'kKNϧcV*F֘9Oǻ*5,<:ؒ|EI$$G4{S#%beTQ!Pve6^\FfX= *n>Q/nNQ#64E^~`G'0\PDL7؃KuohsyQ*Ss;5XvPf8u[˜Sϒ4s?'ݭi ;n)tYG.K|M6Au8'Uҍ*seܙL)e8|W#%}O`|A>bmf\sIO##y3B#.⊠}աnrCDb+` 왭n>Oqh59b f~~V#+b^Ug:^xTq(Ū1y{TzԲ?[s 5Ygk qjWXv_WmMuZ3Q_{=㳗(5#<ID93^y%0Ij[J3NMT nPG4_o=BKJͿﹹoa^ݶxe@e8GHGƊ"ް w~Sav4LUz+fy޿#./wWd^PĴYKAq:;G2jT wC{Ck5Pur0sl+3۬#8]ŋT:$Kr0(\>C ]m9H X1Ƚ7ٟd-KOqDˋs#%$̐@%"iGn gsos+-m%b"moffm{pYpoÆZ̸ 4bx^SuzVlbݵ ϰPO:Zn(#{7)׹L σYӡ"6zXbcG-7J-YSՖ#.[w_#6}P$RF/:HCCo:T=49yzÆrĕ{R4Iom3]Fm;uctloz`(]􁿍5!.ZUǤe(<V+Ύ3Ӳu:b-F|WS] طM(W:u:jڏ̣HEѣ8`k 7c<ΒW=Sq45#.-ڃ߾E3crĞL^3r"M}1(gD%PI`CȲ-G%nG5lV(#6QEcrͬ١C8z`Cu.7Y`D,U GRvpOg[\]EŚ@Bcv8]ѷ7Bov?U?o;2>^R F\ެCtFX 8,;hXj$8Yxp6p ݤ$A7c%#%%"A/c9X]5GY"E5#/*9/Q?Bujm-&;z|ըl@'(*i|GSөK!7mV>9&u#% ջ-v޻ #.cc D@?4#6o轶j0**ŽsÇ#."M\"#%gޜ4yRb(>@tơR]eѷsvӠn(+VpE+'6N3[(ZaɻI{ax=9"1[Jl 1H#.%O#%#%cmaI#.5ׯ_~?OHGTUMy5ц6cjb$|_Cu0^~c_d "w#%>בj>ER=Ss|5mfտ.4|j]:;n>:n@ )fw#.l0}(WoNs {G4|0t9bb?Gk_f "@kX/E;fLq58d*{/D4tɚotgSw>XB<IMmGu9>zs'^͡c7B +Å3r\po_P=T֓4ZMI:ٜ[+Yȍ{Qho`#67]Rd1U[d"b$#6Jk!g۪Lk $,̣[P&UU1#;&bA1dy%* "/Z7cuBsr-$~mL:zXU Dq#6ߩs <=U~L R5} VI魳Lgav_EF -G8?mRG=bSfq7Ai+s"|;O$#Iai׮[H[#.uƎX[ȗJ<T޺Qj@J.#%W&9oD@#.cA,BCG:lkLu#6ž ?; k#.qvʐ3:^LUk4#6`#.~H5ٝ>}[hyѺBJ2\~de#/a74YUCMs{cB'$;ǩ8#.{,jwe#.r^¾<0fx5&Ī6PcQǘ"5UE7yl)p7!$8>{K2ryUD#lwz=wʋ#6Dn}^})DЊ]uq۳2\S{SXY% qnA#U]=@tj&»5&fpK Sd&vE}zGÝ*B#6 Q>Gpvۅm}M *0p|AvB0;k[#6h"# _ z32&Bх56ܦ2,,|زR"QY ҃ LkG>'j1*uiS1u\SdIiTO6Ƙڱᝑzl_g8hxMX0-#sK5?#%yx)7y#%2w Pc  #%t`=!#.fN%[^v'kQŖ`z)nu#?.IpJ[iB*, =`QpgcIezC,f{2ѪRu 7ӝy_>vt|9VKR;#6(<wC)((Fv02hЭD#0+C9z]>lJYbT-žbFrp*(~sꆄ4uy!j#8sy#.bn=>tzh\bPY:—nT `턱ֽnz1DR+GobѣU$2#t^mO8,}YEω{^IfxOpLG#.pYE>vKU} gاڧ^ϭ+#%[a:9ޚ9d+B~e.wqalwE#%{j,d*1wy]OFnsnZ*4xm1|F4sʌ֞{Ζ,l D {#%DhMD)$CTVʍ}14LT.%" #%l@'D188#.A$^0Ʊ\h${ޮ#.NUzJ);I?$J? v#.TTaƻE5vblyA}ɹRC)K.(sN\0pS"o*!WTCyp2b2/mkYKn9J:ܭ\{oMh]ŠfXn#6ۯd ә/Q؅EKNW== ^u?כʓ5+8~яZF0oћ48@_*c0Z4JqF=t%lnb昦J(Bwy.BjܘxHQT84c#6|md@#%奉c6PaFĈ`]w&q22GQۚ1R^+LZyn@m2(ʼn-eFK`(euD 8IwN`;4,:$VbV66PEBD 2NY;6}r=k#6_S0*Byy,SX.9u@+#%)|@zX"%F #%s&;zi|&툝0t651L=õH 9v R4{mnp+=S!t#6ogctnt,luVFƔ#.%Ʒ_Ns*\8r&AڨN6KY#Zԟ:w9Uf j  0F+Iu,<HF;]=ci?k]wXNړC$#."oo+w6<rgln{/#%ƯۡωzY].Lkk#.Ϸg/!5J R%KAdz8vk#.#xEȿȕv`Ejܡֈ%#d{s1܁Qe&rj; 5w1#.)r i/:'D\lm#.2!1M&F)dXUK@md2,tE4'F5F:Ep[̐RM0o5"HJ)#.#6)$դo.؅)ڊ"0ѥbQѸ&b̴cu^،j5 F=3#. M C]o/~aƄDb'YCB&gLF[oQLi 4˃Gd78H]g(q5L;ERi\Dyb9iȦa<LwͣBM"1ȥYi]\b/]Ns p#f z܈F߉  .I۵#.oQvyيxq=LXM7E+_9x:OF#T`ܳqCjm}#6;dJH!MSxL#6\*"e‡;ߎx#.q#֧i>NcUGE^ۤɎ4$;>)͙n$"ȵ7͖Ѡf1(vhC5ל` {ԠR#7(HTZ'cJ:7 Zf6NGQ8@sIۤ܎j#%;qnwj٘Q3ai(Vj(bV\M0kD>UzpNzb#.:8J}COv(O 68H^&ץӳ?ty#%#6]V3eGY`I·% | HsϺEH:u) ,Fj0sVY#W6щ(4"4Yadt#6#}ܶ`5Mffmj (#.r V;'\S)=Ǹ#%'܀#%z\n@|ʼm6 7ǿe)vaҡHFXw?!џj̞xOuw4r }7>(8_]`#v#%H=UQ0rmH%x; .?VI|gxq[$}PT=]#6 Lk+ρKd9r_bDA( #6#fcBt~jl3~inn>y{~] n`"1{Y.HGs$<\G<Dߧ=~Crqy!^HU$2n cb3Aa.Gc@#6\Q\R9U)uۯqV RVܰJ#%Gh#%f,Q߷vKx#% #6wEBcϗj<}z4:C폝7dBb3h"%D͕,J/14iW vevRIKRIXj_^V&.2SQui]!C^~7^4snu*䔩5uw?%`ѩjRD>=>u>i>:##69dc|Vnm/~J:3#'$!PY]oRm$Z&֯SQUQF@#a}Cuib)fy&D*n&NSӰ%$T^%\wbIlI{VTejŋ:1&WR>2#.#6q4~X30~>{P?F(,>_#63~#6 0\rf#6C j1T,jq#66Od'@h'$K媯1 † ː=r2WPŌw1P(ډPΔVRh!0VJ@-I[\T(#6N=>$o-#o}~?S>}+Sy-[O?8[FDctz/~7|6`V˹G8hqp"#.$`{zEVsbGL<OeпIG=}},k<v}#.i{+p߳0@{u#uwDan'@pTc#POR1@y^P8jenvaAcU&d#6V]#GS " Kw-4Pd9p.Q~9ra#.xR^ seHq(>9{ϬQEVp2/Xu|]c=!U%i?C CcXPRvą24q`am; ۾_\PW2ī> ~>=TM@jj*B''%=-uHk8ܪpXؼ&>]Cp}!8BvZ>-꘶*ĄǸ$Mp9@IHg2q#.SuFf3yXx{P-2ѰoIO$'ѿƊ4 Cc 5yo|z1QK,,$o_Cd́bWά ZV3XCE.}<t֚vW:N#Ka<XZ0:44n')c*(Z1F Ip[ ]E}zԉ@x#Muaͧ{yo6lTG76}Y]~z)`]$Ƃ=\YS"#o}?=;~cvφ\X!kC~8ED]mR>^&_#68pboח;vدZSqiojgmysrGt{vǃh`G7mSpa:H~ Rj9\RA~^]%="x h^LGe#.s-я3ٍ̝|%n|jWuz}Fp̜S1LSy}{`--z9%[5v2;'D?/?؃}f]wT(9=E)#.]YYxpaE3ƣ0ѼF%.lA>2.sU?F6e2w?.ſ=nn;"^N]i=[<JsCác!-96GM\RD8;DXhk1j|G#efzu?|{?b죩]<q2yW4sf_Ida2#푷#.+΍-al޿(/#.l<t+{t3./ڠ2@cL|>y8bwc|{ar#gwv[q$RS>]wtf֞O{<Kjtc ]yDZFդpzA:}"p+QՇ_^/m&<7;(|]oO%~W2lS7|r,vENJ^[#=6{}¶um`~۟]n)mϪ#6GFq^F74?w(?1脌`O)[" ƎH#%"2 P~kZ#%RE)#%:O2<~s?u%WwP4I~%Iiӝto򚐺qiA@gjav{>u.?Bʹtg*hDA SyGbe>_)n8h>#.[gj ;0v=>14x gP#hp_Y;|PVQ=?~umA#6`Ǚ+Ժ%#.Ó Swt/vn#Ͷ?!bpv l;̙/|S9? "(H;G*t<&/ֆëGKDj`9K#%#61T'&D񪤽[-2+`IBfH&ڒ~;A΋wŏ8,ȿ{#Y^tM ώ7},Ep#.JR#4Lh+w.X%qh !~I}A7w!h; ->B) J҈ǁ>;ZZ;|%9dCPSxsZgDswr+)1/1B+Sk<kQ}=:ѧBx&"8K.iYk BIeWR͠z4%fj|x΃0Ϩ]Rbo<<rga^Ǽ;y-Y&aI$": 8wu\vioMե|Y1x{]2^aP-zם4-]l_3T*|)8:aΓ>_!(F+~4sgWM/ݎ߶N3hl3-m=Op>#&Q0/S3>mMw\\} $wyR$|_ID'1uluLׄ[=8'x3A#%/SCI%yb1FɥG*bGѺC IO4|Hc cY|u>S#`{Bfe̍cv9G?GF54iTlT Ԣn؀7#6҃,tڛH}=3֤On `a-AȱaILA"hhn)"4[VWc'#.Ss6]&6)":[֡ɠ\6B{qZ8du["?9gBPP}S< =a5rA]EcXw\1ac0]WoOS\V##E4DCH@bi71lXTQ&FCV6ƛfJj*PYO9qߛrӛܙpAFތi|-'lDjUuwu_fWVl:e1ջ(LS>ܬ9H<jUm}Z bvU&겷[GmCfj#6#$pw]uG<~ӧ٣OZx#%8g#65U6;>aL~Yw?VPwu~Uw\3ܮk.&5)n|?>&nnM>MwL[% "B<ëά#6)_@\S|8ћ^F+rEh[J%clX*5gtu^y9fRɢ(([\0ģ#6iȢ chbȣ44W VRңFR\mFAKTQ eBlB#.Q tMy3w%r(*`2shفX;g6JhTCh% 8+:-!itu,#.!@AFY٬uDm#.Q$I8 ~UI#,D֖%\ȪrF(3*®ʌp#R1lbJx3!U$IsJCCID&A;/N} -LH^@u3D>z#.QUhzOT=gx$gSP-Ίj#6ףLPL*fԧ!3F9j~hxc""'NOU876 VY}NO#j@T9s vA#%_${ӫԎV6T=~{ml_/ٵBč'pqC9ˋ!yI<쩫.V|!檣#6uyC'>ܯAe;6LǨ49CHQH2t2wT9"+1r_uLUvhFꑀwf\F>[QZ%T#6լM[he$#܂#.Dl˦mʊBYQYCFF7__{HUu;wOɓA󭬺!BAT1sa虑s,]Wh&6p41-6nH)1=jo\AyY=Zﶊ#6gApE30BHV*h/mM}RdC7y#6ڴsjd$ 8Amb{&o* H86AUrYbZէhCZۅ2SSD#.H#6J lXdL1a4KD#.G3u#.1llX -m(Uzhɐ1(UX0ؔDlp#.i@OlշtS'30<slPt⢲o#6!B#6-o.O6ں<Alqgwff4b}SuT044V\w:{yзc(~Qɨ<ޠOͫчJtzA'ku#%Ǐ"#,,6aiUSҪ4]v,DjGHҌiO|UdNG#"DfV5[D*#.m4&W(_t53MСA!yK#.TU:]*EPT巗dkkm0TJ 5l$2~?sg:?.\`<<@6>\GJ`CT<2+riM -+ATcz^ǶG=4R뫏65atHNj0 FG[qN𮋳  <6ӈ!#6^"X<(Мk]o3H5KKpL,+&,ʁp#.#Za*V>#%h8p͆K9e.lm8V6>d4#C୶Aܺ&F KbV4 =ݠDn$DNAPӌ+gbX̑FP2-c+P)22+*vw?zw_9[ߙoinAyb'j3Xt1m,3tz~a#.6uqMۉq9g,;͝S(v-`kb6V۾ϱ)`f=NRAUHtFE1OSxWxejhSiyif6ܑRn1v6DE qT'wπ.gh#i^Mh3`Qq&ri3haԔ9bXAO}`PwU3~o_FŚ<N=,9}'pF_smA#6#.:M-ɰz! 1I(D8Mp@Τ2l+"Vq*t;$io;n3ن-όu#.pQboyN?uL#.}#.IM$ x)Ϯvюn@:k#.KiccndԪAj[ɕP]K)pU7U"wW; ߒ!r7 JQoŲmZ\IAƆQ#6:26#.];C⍶볺 K%cTnqQ峺m!X kַF=ˤVvNSus'&vl4rb<vjN19b"0NrcZ6W$dB\֊wl #6Өc.ld6&Dm<͎c/r5ߠՁo:ٛD$o}䫂y<YhՊ@μKaNWGi/_ym,qGr0GܜFDdV׆dIF6$,5\m4b-TM^"R".f哽wuRM|an!ԥAlK<F)FP6,-e4RoNYy\#6wƷŀn,ۧGtJLL k8!Lm?oxta^4@en8¹bUֵim;'yhz7,+ɫyDCTTo89IY<\`%dn;,B68u67'}^YtNWd;=/fRͲc pFiUsk[377И{1F.L4<n#%SGFӃ9fiCzү,6bi#6am7Of#.5`fzץqau [A(gzdGߨrt5:'cvP$۳Ftx]L@VA^yw17sGqAكdf_ģ7nh'8u,.y0tO`DT#6 3 ɤ@jP3<#.FR/(?7G:ix{D#%y#8=l{}r\́> 2䀹iߥCKWnר>pbx_9WΎ@TBъZB얔b>_cOVz|ymx#6fǼYJ- 0#6@wwvOӮ!xnpFQ#%jǷe#1TRV1(340 cpTAϨ9z"3łN<ycМrv>ZQ:%Д~ND|Eɴ%< &فq4 )Uy_u«޼?zg n;q;҄&LS1eyg)IV\.Bm0J[qkdŰ#-`Rb]83cO]B$K|\ț|jg}'$jaȵ<4=6=&|6]OvpX=TV*yum+1e#gDt,DFQiwqlޟ#.س^O.Ϫ-I]AQ72I8xLjoLk.ˎMDz=t&:o$48;!Cpa}j bݛ94g|3߽4#.O%ԍء:<4E#.543RTW-kycUN-<#.'ik;!:-D7)%#.M;$h.ؖCpJi5Z9#%7CpC #rӬGrɧ嶖9EJ͕aՅ`xf-0%k2#x#.|^:+Њ S9lVoaz3!m <yCo{v|edQ茌FTĽ~;wjl .r<0X1`1߹i1йKjӧ\;d|:Oeѡcsc↛Z<LQE-a H 0N3\JܘrSs t U>\}[crXZ.,#Ώfi?'D`@#6o~h0*:֠hKCi|__.71u"rFxô⠘#Lҹky>Q9ȳ#%h(+fLmh}uڎBN[R fJ{2APjפ/t那Y$yP֖뢕@)է)O~U$BO3ud1/5<kUxduta>y65NQpNHe>D.*Edn2,Y(迃f/sқ DҋؗЦwNI#.PBbVu]Un=El㋷>JE":ǔ̝[~ڋ&Oĭ)%&UrfIJNgqaCFpzPv߷SC:l#:=ɪ/6<0DUF9R3}m6y)87տQe#$\Ƨ^q1J=f3k$L{=Jm&@׆v|^m"X<cQRHyr#6̪\hlτ#6Qɺ*h952g(eG=!B-F= *\[=4ʅܧImЊFo%*ags7s>:$qCntl Œ=DO@};_+'绠EFD_@ʴ/H(%ۏ~!ʨsMG4E8yKڄc66vvl#.gt>";mu,vut=;2h!0H(ލ}s^G ap%N=cQ<dZfW=y[g|̉ÿi2L7#.nF0r I8Ť6D$M/#6o@ HܡYeVrDD_MptCy:1_G h.:zӫ&~b qMN6kmc99ļkgX3sStc/+F!2xX?]BM#%R3. s)3F~~! i98ItgHCBʠ@FEɁt xbT{IZkΟr[J(7Zפґ齥4%ngWUp‰zƚ. DcF\sTfW#6/9=jѴSeEB#%Xu֊N,&6#6B=p]ٝ#6f|:DaF/+6|Շ,2'7;5m̩]`x2k\U_vX),{wY%q\j|x,`RWnưM0<X5Uo6<-)d^b#%ڰ[>Iz]lS#6eh$'Hf#6^UЃt..k#.п46 zXPť\91mRt}yUQ}[;os-<TvZHsĕ寻&?ߙf}>W޳Owbe'qt_I&5q"gj2E"*[Wn2AM*aFkm0*e"벰I@.q|%hygt>G0xQZQ]~Lf9hj'YtX?PTQj?o]YQf[DԳGtP?eϤ;m\4x5K@Y ([u`"%K%b5j!#TR,Ы`A5w,u1K#6u+;zEko.V߇>(XV9Cg u<w]7+#6~Jo8#!PJ#%)W&89/o7vv- 6mC+WAz)zV{u|NKҏ tܹ"E/séWaƺ\T:#%̹ovJ$oe@tLE#. HЭxiz @pyM>gV0P/WF꠪gR0y\ +nSGJՖ^IY}轄BYC;-|CDfVs#%5\a? vs>_5Fypfuʝ#-/\Tz>#dq K{e"ϽF@}#6#6pi1Ic1 |Im'T\&Ilgp/j~?5v ܲ5b7fnN3[al@}sم_ XFۄ'm6)k4', #6֕KdUC#6F6HDpJWSRs~ axƫaz57m.m*E$ CAlBIm*ҡ˳545Jj9}xH5ZQ<WNVDhU|f+<v ^-~]BA`nW>WB(P%D7K#..mLj+,#e3m{wl4/}}YsEC9H2tflkAM1C,`lرkOIiN}#6n#%A^(l!.o7B[8fXHڀWz2s@ɓjcoN=,qkPx١˷qX8~ =ޡOϒӯtq8=žxE@#%mpONaj eTI*xzԢ4lGGޏtОq͇5KVxd*W6XSm$H/᦯FS<?z̈GP-;yZOD5˷giT 2 ry~NKGkgv|W痿~xLη1\8} ӮW6jS;TǯOmuW<ߍ38z%~rĢ3̦ۤq^Ng'uk//u65>Q{he!w(Ϛ]ȗc!OZ_y1L6P>eza_j~Z^iJtQa[›9-Qz$oL saHR1T *au|bȳē{S-p5cާws_LG偎c} 5IjJ#fعM$rKLgs9a--Jɖ,㾛`|r]x+n/՜ELmb3O]a(!^\C-(r"FgRҸnXم꣥1+/!ƚ6d#6P dITHS{b31棆N \`- xf<3#6  g |c$S@g"Ġ:9.mچ|[6jg5s͢ٽK=W#%U)AMU7/EVb>|sDD#6T2y qв#6k1~6,%gK"hi2K;e#6 fT X]ӦhvְNZPqWPI| gi ㍢\Ҫaa8ibPJrkL0v\q!ytؠ:ܵo<g=o6IޏblHCZn7Fndʿ_քVMc:v+F;ܬ)&#.M'#.bYw:$= Z' &hhmZX\,ʊ9C!90r =޵m3[ ZWT+(a 19ł<搡vFx~GBUU(;}n޹zo]4B:=.at$eJ`r=q`iC JD#.BY`fYBAy U /v/C3$sk:!r=.Y8=}[SdІ*M(RԐx5&GԆ)pI{ .xGtơ',iAxdtY7Fs<dWߣl`)N}͙fKH}C"qQ/Dqh%YNuaf6O}_+s$;zm$$LLzb˞*'%$i:tٙcysQi':-'(`b6<-8.um]{gj9 ^J'o}҇D-W8g{mqnkZ99֍, C`N`[™y#6Z=s'W'd\U|M۵ъ{zrEntwi0J:m O z㍉-|o)#XW>s=o[DЂ un2`1NA;o$x<EqK/ߐbK/ath$&c%nl#'s(Tiاۤ^v*1.)F<eZxA)NT(AБ?>?N_X#6bo1QbDzҸnBhtRߌM~޹h>(= 66M1@|]f.s۞(::lzjOk)pVzקkj3pV>If$J?LV2e*Xp平n(#2Yzk09<-wu͈lnIv("`Taߦ:bdBzGm#6yh3儌#.v=Bj+8;>N8dݺu]:D~/Ү*zpIs>ӾĶ7|Џ.Co<Ozې5*Ќ}&Exъ!SbGeFI-U#%$ԳIٙlM,01*Fe` S~S G>2@~3 Z/sۤzk.x|~D#%鮈u*Z#.nP>uxiԨ&'a S92)igkH|`ܡiS{`'+U<:I,7_ǍV5O/I~!ZRpqy^Km$?ru;46CR;,VҤu'/0?5{is"Vu jO3KONw/6 .#6XrpoH~!RKz%dn()!ߞnȀrlGܟ#DΎN5wP$~tԟNBekM n 17_޹?5;{Ӻ'ό{q(#%`@)(Ԋ9Mv#(JL+G;I@zxԾ(H)gxTWƮ] oEk\lIi?KWؔRmc-5L>(2͛bFmpߋg~:@4>$_#p$ Ő"AY &ׇz:-&OySg]ܞlf^ʓAo?xG57BH\N91IY[Gt#%qHTi*7+?YCxa(z~[_ ]>Nt"rNK(zKd!ڇEtV}nCi]Y??znN:*F`6$t%5Ɛ󳃊\#%|ӹ1?/Y8K,H|"~Щj!#.Kwk|Y Y=v&S7}fhP#6̷ Ϩ:_B>s<7EaVNK](Q/~w+E@L0\"XB!?_G7Lz߄m>+emf c(QƊ#6"u9#.P?KO L09]#%n?@/T#%tx"RKTgLJæhZ\j +r Lt#"#%o(~#%cmfU#%@ER،ğxiBa`8&H6PXci K$q4a-)>N99‰5v8:je`a"eOX2~ZhCq yWiƺ(Tۿol|H,z(`L${9gWR|1{՟@yi<ހ$ LJS(OgIեOSI#zco*(Bv?t,xT[]3X(~R#6`UMK;6(ܣ؊y?= а섀> aI\}V$42q me#.fqpikZԷ(bRf%ER1#p2\4SywE9g+m!#耠(&]#%iuQ:vٮJ5I4n2mp諡nDA|],ʈ[#6y`#6PIow,lN"c;Y:=Z&W#6fY`38Ȋ7K-T"EvFQ`\P.Dp1Cfoe_}gc'KT֨`XD$\#6P8tbOIan0a֐L bB#. ]\BET d(so)g #.4F̷e`1L`yJIG¥#?YzK2 #.:7ߍaNQi2}}Joȝ.L_fs@v#6ĥ7;_{ɞFy"]yXm~,@yNr+(kخ3zL;," 썼BN3$>q6׿yð]|'<@HZ [*sX@U-0e{3ӓTi7qIy΅'V?rH!d=1#3te~AN{4N Ӈ I5(tD# uՙ`QP+&֗`KHZh! A (e<dem5^ޢ)?̸皖6`{kdVԝ2S;7N@`wd@`s59_EOR _zqKs^Gl<\BÖBdF&!\[S~zd'$6٢ɟ6NX |A(噘r;Xq-rI{@{"{Lź5y\JE㛏%9!}CI(s=>X.~Xp^{ka%&⒒Ww,/mzN5ǎ/mK$j +K6L8~1o'\8`-#!C<׮ l28XNeRb/!E75pdpBq;8]c5 ٽ= L=7#%X%3#6,4"uo8K<׬(E;UZ oX:IMejyyBBT#6=q<,; q̷L-rj~Ă=%u:Rcx+#.Ve(Jź Cצm"1I2*ܼF2?0k˳ߔ?\Cc{ >儯p/UM<g;e#6,ۡ(zbJ :7㠜lG1џt|<X'M1I /T])o9_Urb,OL{S_mTܶ㝳ZY:T9oƚ(ul;g}m@ߑYCfMjd<#.x:O)m/4@[Z; hhhP ;w҂V_Fm?2_P|M9ܩ|r]ko {<xrdLf,z7g>Px,l5 =J{CTjhbcDi:L$eRItt)ߙ~t#.p}w)@)) 2dt`!. 3Yb(%D#%tdb#7Bʩ'ثYcCgMRa2[4 ]gbҕwoNݫ,.1DXT<5'.?*biW-c@UT #%AJ#%? >[{}aEaE(ώ?Nx` ߷"lP ޷G?`l= j—\4ʤAP(A&0Ax2EMr2I#6Q0D%90(^Cg~ەRT:Fgw}}vT$/d*>Q4CިوVTńKv#.[T_l6q :A-E. x42#.ދ|¥qڡKj#32r2Ț#6<7Y1֑$h>GTx~2@*%39hZ[`tgؒjcz! ,fǭٔd"@2 v0k(!h[YR |"h/#.( (00 Mm>+dʈDTKGE@2. &cu8 #a j6MIFE.3+;b$K*ْTHBn|4D3#6ٽ3${O٫H i"Q`UKs;$>5P({lp?),rb"ZKAƒ;5Wژ O~~ON{Zc#1P'O)ϭΣM!+E>T9sΚ? #%`cnh?+BGG%om_} MQ' wGύwE&hn%pm"&w&$)rup)\uUd{Kq E]R*8#.6lTx2iDLj22JˑuP3Tj :Ŧ%tCA0۳=f[/0MksvLoTw e1 C{R_qZkc>afоlJBBr';<[Kufȫ%3Ja1b{=G\ULj#ERX城ncsrsVm;X;MV ]#.Zm2$ g-$Fñ L%._L6QSxݚ#.I!M#.FϪMCvB#%! qI*tmơt:C\$@0!#6%BeV7vdcA֩FvtFem6ۨ_^q3_évjᙎ:7T%#.a 3#D<d!'=Ws= "{r3xXL#{F0񜓹<t'(vU\s[%kUZslUʮ@K!E T$Uh^eeq0Qj6x$AICtM4i@\!{!xt,cߓx ltHh<y̯wy|3p+4 )I HE@;4['H!ljQ^58u9^Si|E_PEܚ4ixjRdF[I)-|݇E#.BJ}ؠS0+Zc8VrQ5TQ[u-*gu^24ytKkv_\hG}{NߨV I tUb*ڧVKf,#l swB1Q"ݐ N]S}|x׭?=,%xo:!I*soɋ1J:򤴡eB#.#%,$9gnyϨ7#%&u$36;7R71)T$#6!HTZaH $"%;QҬ߃(Z"(MPB0ˎw@Ne{]=d[rnVoÔR@<$D DG׶-"+joaeSUX'҂B,ںKԸg/7vjRK]#.e+|xzp®ބERǓPig>,PC{Ɩ򽘽Xܩ_=qAg6ОO_*P!6E387Xd]#6m2M#%Fk#U@Ty3o#f, ;Ηm0T&&45I$p~41r/#6jwXh:nxf|LJgXCPt/yҟr#6\(<UTȵ\[#%^H#.}qVFX+1DӭB#..rmȽ͗Y]ZI8LA5N0$юm#6#.Xa.\H܎9Tw_vEF`0HʡE%x+2<<-#.}ϤyT[%)740O:dXoYt{{U;R %D P0Y3#%*V q@iAFL`'!#AV#(RBVc3Ea )X`_ɝ XhD;e􉌎'4QJ#6 Q`(T"@ K `nƴ Z:uYƏtF-RTfsK>BoH~YusPѿ 9:Y&ۘ: cU:ٛBr0>^i[d6p;6 O#.c>U o*&L:.$Lx㤴~9'tld!1#.@u*n&HQT*C.졉=9"E.Ԝ+ueHA$}{;~J\MDF`aGnJBP=#.ݱcLB"YYj@=<q!ɋflc\1GVl.o6linD CD K)Uf[(8?B^f] tJM/BC]郯׻i^|E7u[dgZ;K` 3;`n 節qi8׏OB!2CP;{]Gʂ0QvD9Y=sEB4:vAC2.\|iovZFQ4ZOi2e671kk/#.]6@4x3telG͆ђ@R])NueVL5).s7@4/:U]uC<R\w'UW:QvbFH RS,w=uYV\<wWa"wߤ # q.#8%vωIu-?8>2RMģ8{x]zB<bcz:yfGhbVbZًU%Q>V'׿_S26_g#%:ɩ_xy#%Dه~J$?~~8f>=G/e%A7q,[Yob΢od݊4Cę?a?o$6aH*2AHe,-3W>|opgrjntK;߭%JMYTzQ<u`XXCWKq_A?oMW*ˤ,/70o/8qI&eU՝ωkR2XIlW+YsC?۠GeݞYg |#%$ǡh[<cOgNk¼Mbf"\ 0{P?oG)>p?y?=BVsɼcNG8(5YyPYξjv1}+sk6dص2y$ܡi)_?_3C6?LG_ȯ5Mu=!~*+LOk2b2V 7DDa{';(#j%3tiץAQǎ2Yhqߞw/Z7#6uiMZ0XL!9zzYϿhnHr`f5N$f"Vޱe>?.3/>K6M(Ÿ9O K#6=mcᄡ'J1EwgO R " m"@D__q4|`R쎉+j`Q(L&‡ПK#.FM=33ISإcҚ ȹ~>qU-^uH19aY?NNѯ]#.y `2/ÈDϐ>#6m*H<\2=|.mcw~w`PM9^j=XŽfulDR,BpRH݉)_Ůߦ.|I MJî EO"aOA$F5xqFwd;zС.'g|9lrl:vGi2]  @  3#M.;G9BG8\D=h^=k<RDD` _"xKyy+75]*:p#%"t-8j Ri4i><z%LQ>\3pӰv1go cj^\:Hr!i ~ZTװ-(Lr'@ٷU:k+cJ ]8<rɹ#6ȄԘ^c՝gqoTL#6$8=<1c4+?Gx#.\Zs ܟF)Ehs.`.z0ʹQR~m+0._˗Cn5utWv#60蔪Z.0𷵍SFNON#.Ee32J|;MmQ vjxrJ~Ѐpr)c~;woj1sTh#0!RUW}hb ex "|PH[ǧ:J/qDCIuG_z8XzgϗN:ӿxaFӤ̺["ejMۙAA&6)BЁ;XG꧕('ꢵIB(~ a{p5. 55.>#b=]oGTmü#%AP1@zsnl?]ުzxk@2 e*+h۝:wݩΆ~ TJ^9 ͑4hB&#.xbP;Ԣ^}؀K}!I+@=@- )"-a7&#.J+ǢLp#.uڅ,f5(TMB9l05Ւ:sV6z=or,Ư~\ݿmW#Lv#.{PP6).zE{an!@\N*}W3S+Phucm Ӈ]<y'ztW{ڳ`̫- XLm# FuZ侵9S9}8+n^n$dE['}tѵKQm﷓msVhU_@6oOCUsV:JM,tnO伎r'JhϓW* IJTy`,xPL*3(#.s(5.w~>>j; A^ZՊ0Mǿ*vaF7#%g}l-az3ur#.?̶#6!8P-l]EǮZ[RӇºH535qݥ(*SvAkܴiXE%qan_#%Ee.g}H#lk>.ԯ-(2}T|p= m.C:)]ݐy 07aYј Eg'>ELNz1(\xΩ wiYئe0wԇ%<GzZ aFK^Ԝ%zdM5:Rcw{WhOoly$z 0mCgpPh UA| !=yyDzs9UI#I¹#6Wegz8V)tFBXfG|o.l&)pMSӟ5#.2sQ"w"ju5N7}uKfs=}؊z_CdlOr%#|*gsg~D\͈zw~h鬻{xHgP68ߍt[i(NIБSmwts:'?g6 z)=nЯ[J DqD QJh<^gP%mn=3td2ïATd^soRj=\,c-#%óx; WxzX\3] K^G1ժćk{1BϧY]~<%HmƔ2 U\ E\D#6=E̒㶉rǑY%f=M޳KtZ'l88{6TF=E|"#.UGVmϑiY'. n(h\jdWMأGX|^uS& ]^]s}GPWɵ]>~+} "6U =c8j~>?ss"Cqv? N=3 ύDs\=y-&*J[w bDfNHZZJQb|vꚊz(VtAq#.6r#\g},#6NjBp~ÇBY7;O߫* =\Fv̉RMeE\G)Lŭ<];8iƣ ] Y\6g#%U{[.I<:g#6Q.AQ7##.h;!]X0mgݚ19mH_HGYӼ?NwQ:)$/Kyi6+vJEB@ƉPz,miKeAx1_S4`wٍN{PTXPD]*DCEYc^FOz]cpUʏU@,#%pHmUWa*|HΈ}u'2ԤGC9BSu&35h=3m"|\!XP.{yH[BJ+GPS#.O2,,<ӿf;rM`)@9b\m@@@]ئD #_KcR(?#QQpF#%:U#%"m݄#.4(^(EJB녋2 3W\p4J#.q&zB/!<9#.lcwMzME׮#.CEEE#%<shypR#6O}E=8{q! =ܝƢ۩)C?s_]#6T?@-!/MJk覱{Z-@?U빙G-ǟ@{cnLQ-eFAk93xx/4PH Dү z@Ux#%o22iz k$~c5o}RH1 4}K LvJjJ%}AbArQ1oڷX#66H.>q|$T"~$#.YqrA@f#%W@C:Њi&s7&BOCf+uj$ߜzEO$>hntH!#*1".N;]\xVd^ w|gO6;J0&5#%}8Ur/$߅3>x,Cq;@v/#%@]C9Pgr(9 v k$9T4h ~m*8꘻` #%X} lJ^. #.JCVg5t^7!߆ ee#%~zT)AY`N U)>S׋rYXP\!&N0#6zY*Mt tl,tgg2jbON@q7&F)=(({;P'+wV_?tf@nX.3Q%x]]v[kסT4%e^ROkl(FX8g&w!?g#.#Yj~ieu'ݘe;gTLq1 #(#,ptA5+ldw{v偐;mP'@}o=p2H#6O G_0P&J#%TO\jhDpɓêКy9Srx-hvd#.Y|PmS_V?L_"'(PvR6$Z'x=#.A='TP)%}jW\Ҩ3ʡ̸|Z~cF0V(i+t|~!s#.@Zxa%GK1*p'=6qK/ x ҽdRl/UdAr#6+G&InBs\g" _K?Ma{4ÝCɸ~܅zN}˝9)JIƀf<w=,&M!.pVNteزӦp1ИzrŝXw+8Soy1,#.0#йB9:-{~1[r[/{"V*&dqY-.pO5w;- Jff ǻçH|Wɡ!I1eEֿ9AtP_kWHV#6%嬭1^#0)Eg*mŲ VBo^E4yְ. Fz4`(٧W'fJb;:Z;ne,5_==:8*_oz|q5v]Qs.2#6Ӝ~*Eۑ"c|T=<##6`-P#.L6 Ѻ GTt6k^mur%D>ڇQM~p݌,&_AgNa{'##6ʰYȇ#s\-~mɱ*6iI@PUG/G$YYzR0gC~6SP^Ӥ#.zkHmim.3xK~%"+G;Lnc53>pxqd} =ӷU Avrv94>LpgL{t6{<]^ޝ X \ ALo8P6;Ak#.njxmXE- ">/ѣ5`[R6q./#%Pr9TX8H^ѬP~ZS~!<פUO'U0°*!5`Y$D#%<珟 A8yF*$PIe#%Z;d>{/ n\A8*#vw3*5`,r5#./܎4WiZG魟!u&(_qDg3$ga:,M{k!R [#.Mi a6`3"N+bvd뗊ZO9$-]ۖ"fpELiUHEe.CgA6ygÏ]wU })#.A`lAd9j*݆ 渻,h ܣdw.dy r %(seFzeڭX#.KiC'~Z6# sYJaLJ #%39FQ"^D6{PSS3yL'n's*zcT#. 2!r(PhP37nPF;E6#%-}]Rk >t[ڍٯkY,oŝѲZf'̆F"_v8<[8X˫ɝ;ru06fڹ$H__B'sW$m#6E]}#.~j4a٣ Mf \=Jq΂{O jATP}1%"͢1#6HsMǡQ5̽9>uR#}3vagvmZU#qVXH2 #.֛@$7c<!"2. !҇5Lڶ慙`Vb#6#6ϧ(݋a'9#6`4Z >P4i~q\KA!nȂ)YɼL6޼44m3r !Z-*5/!*k VDK;"n[z2b}ֽJ"Xvh{XfbZ辴+Au~Oڰqmބ:Ca,&q\șA̶n<l4(WΔ"o?wzg\.f]%kْt<#.ctgߛr#%s-  mhŻ]N4(Zsc.x*sgi5JI@od/(&_XCZOV?0d<֑ĉ0斕;bzUKd3 ʗ h@ـQmc`Y N18ؔ+n5#F]"RO]WQ5#./٪~a#% (-+Gc`|^D]Ĝ#6El\q`!z I<)MG~oxE߿F'Qn#%xTL9-4Oxp8Yx@o\DNuWL7COZ#6iY|VA]&[_5i>sE/#6%5/N(l9s7hgTHYg}_A0VSNĂHQ'U GJB3qdZ gЇqq׋_a8#%I~Jڞ796`|6/ Yrh ]3K⻳|3|cuyb3f Cx-X3PXt?Q L#628~s;{|%UVv,pgYT*Br)S0ep>V_b?BrC9%_$TIUPYEZAa#^ؔX81#%h] 0;5 gYv|vvގc˫_wNxA3Caڗ :]۸xn4{3 HYTBTCal<;`xG{ڝ{a{.A4A#.P=+Y;]N5%х\K59=} FB t}:$A7߾]M D|_*U./,!nP=`d_&\B7d{ A qP#%)Xu##\>G+[q7b<*w=OGH|=NA LRzH?yCԔV>!0н^.46#.?ipv7jѠX%.S7pMb64yCaΚ#.̷Qv &P<#6vT} XҥW`_ }9w[wmZ?C` t{s?OqVȨ7R.Ȓ(H0p#%o{x-)F,=ٲ"Ý̄䗰9Nw.icvL0*z}pAZsmy^RONY GT%X|B54 T(X# ee=]H#%wZH_6lw0%=i'`w%#{;r6,#.,ćzdx`9pCYTe*iD-& H<>Rg#65Y$?.xRR=TU}@1ME~ kmmG&Fefj~GM2YAwY #%y Mo#̉lB@"$e |5:IF~"->x"#.?#6[p4* *D usmos!fM*fo5йdƿ=1Vmk86l\gbݒK!UU;I#6lJf86#uoz <:YhyfΙSlK6d.SA~!#6|N3;Oy K~İSf=#.vd?IU6[~@@X#6`/{> #6Q@lΦ\)%P7`ׅC.up`gV}|F1bcC2˜0}"Ao#. '#%h&F7yG2M:D(3_}HlqE#.2+ϭM039!F9-E V#6 `dxk #%O-=fq/ʃ*`k|X}y^E!l<FDYA TD )#3S :iq`wr$+">,q$DL#63kNk^!rCi 93C<2.#.Y$࠸oXw^#%rUvA+RWVoqvތz$ceXj1-.Xf]dmffT! }Ŵ5N*nl+CEגI$5LT˭MIikmjzef׌: '^kAַfuGV:eA)2gY(:#6X#64YH0 AP~_bjTm2sxqnaX;ڟ%tG^^Ȟ˼I~}~ռQR}q#%!>^Cwl9;6Iݷ;d]d>a؈ZjH M)QlЪVY$ rPRm82d?ۍc;xzZU#AIE5&Wf^%S-5Rkӄ]Z̿19l=1IAJ~uE4+.^%6 fg -ԦɽI"H~WN3v7()sp*K;϶3dc_dDv~Q![~1mؾ!t)lH3+:K<QRR"LٰLsMw` ieB@0bRK:Rd|~n|WnG ݽ}8_<'aVQ5o\W^0'h72u%Fm f0/i@P.b+?NTPDs#%>!@Y68x\ż.?-Q*DjIa-zRϢ*%Q<r0}y,z >[v`#.&2(Ab:69v`9_>ʧ86x'7u'{;HA5twPI(?xyX"DN}+ysFY3m;0#6J#%aAD<Y( .pWapls8?~3 PGpC :3yC?P/id+?GɬV7LUQX4=2ݖ|* kCԦjI'> 5UA4d>/ЯHWb)Z]CZ{_\0GLrupZG}6\ҍT᠞20Uʒ8MA#.7; {tc$:F,!::`bq esdQPNJC2U Z*>RQИeLHc;vbNTmT&tKPtUH%HYW>nC-mur6E+n}|=i'2)E^S<a$Ed@$ZЫF)76,BĤJ(~'QvVbJN|=<OZ'Ih<ް#%`s#.dU6c-3#%M_/_k/qcu{??#%#49'J<JĆÔ5m᥈8`:m _ \ͩRJJQkuOI'l~3Gb=hj=u$!Lw!(TCD~Cϡ\ৠTT!S䧊r>2Y"C}iJ;).ٹ h_籄w!,#6|N}C=)BBh"3애kE,e'?`v\>(}M|)k`a]˕aEh#F%LV#%I0% Z\Bah%R2`lHvE6(\Dd󈸂HH"8ERG5֠AC D55-~}#.>(#.<1Z*'Qbr#%*I}kOH~)۴LAGQbY()9gb4 G~s3ܓfHz3J`V߶P+_>q#64X̢3|ͯrҸn[]0Pmy]<(hW?f3M A};`%f #%'YH[#hqk6CH&j Y1#(#.DB2`JhLE(Ҏ*XZO/';+ÓxBJ<9/;3^-i^C#6@գwܟ}RN5 7n9D#%pHo( u}J6+pi:<s[cے. 49 <<gۃ; =a]l#.)#6(߽d`~YIIRJ9\8#66zC|>4~1؊&IlLatɆ,#Z4|kf_ K\ U0$UOqHhG:#Y9!|r=H͊@p2 B#t*KHٷZ<C$"4?}z3ԟx'M'o#%;)B0o%dL,6f(ߤJu.[<r!JS_#6 #%PC2??fY1?I#6z:h"Vwg#6 )ʏT0=iڠώBi0“% gp c%U!11*D\?S:PzΟ æ˗HJMRd1?;c.<b;nN[ ݞ{Cz!R. S#6 =,>zDZN􁷕NV2K.*~ GX5!/KۅةӀyzK*ü`An߉l/g*|[BUBP 4ń8'Pf,͊dď1k;Ծ#ՄQRQ{mzIIJ0ĉÖd1 *0%v_η?nTt#l1绸UgP=`*II8'ʨFP A_NAJ`}ÿ|/O>9X<|z`7VtdH^mQkKH.pDGb\3>:ZA*=^W< e|9 p]-hẙN{qbB:8jAK F#6#}띸O`yJ <÷ ]ȐJʪ%[9_;#.H.҂POܶܒQμ8*ex _X(A=I\,R <nn3'_gUJW(X7><? =>cp`Av}ϰO"<R{8r?OsoS~4j0g+d(Z@A}lrޟ̤]אݮ#%5I#%M$ee5hPm؇5oLpxoJ#.0I ,6BXZPw*o6`>Ϲ#.,~謙!" "? `]Q{jNo.˾0,bd%pAk:.zh*;ϻk'u9V/N|%bdϡ?*=ym+Pw!d#.[*p aln7^9Lйp4#6}>|{Vu-M)mvnEQ3t6nٺn]C9Qp]q#.v˴[Q̚DF-hja^BVͶl؁#%PXV0PPӮ: nzgxDeb>yk>z{h}s1AdH>fX}Ծ͞#8&`w6pFBBM#6I#6~랠H7V%/xn=@E=jQQEV$[jUUD:z⤊^*eVؔ!&&rl|D\Xi?Xi%x< zfSo[֨ s4'cyPvS0"# ͑~{D_*9ոS^t(Mg#r=})NVA>?e P/#.Yl{d+V=p0tԹ20{Qnwv8Kۨzk02#%#.qTK뇱+WUшxɰ *(D!Q4HT,P@X$Y5eHr;lllWTKw{ZĚ?N=g\31Il݃'0`+m@#.Ycy#/#%B._0j%/9Qr#6W=r(#6`1M3-Sm)s [!DTLYߨ虎hq9"T"#6+AᩅqڟYfg (kf:،C#.XaŨklmYq3~73.%Q: v-r 0#6lH`}gtL|~Gnv<! #.wʟ,ð*U0BA_Kҡ(I:*M?ͮ5=N}N\׳&?zФY-*~#%S Qk#%Zz#%W;τm`%#%&U1~{qT{,eM"b!֟͏"qzN|w1eɻD@ y#6#2;₾%2zTXA#%28*5@'nlV#6~\bHQ^螿,[>G;H=iPJޝb$ L.`4m(4^?bS*dp7fM ?A7D$˗t̛d>o#%d*6<<=͸_yaa Lf9mxjt3]]~N.5U4K.wͅGQފi}kCe.Z8=6tYr#6%7"sPDL1ܱ^'y=;*Slbl{*R{ \2@kPᬅDG#6@<0Gz|~HzNcG]bcy҅!!?o?TQ)ލQ6_7ۃ#.ڊp7?v~AJ'b,z@^@q(M9T*.vmº9ψY t"&;:<݇L0{R/$3#6?>_u,6捚slsl˩0躨sG#.uggmPkl$XEvQ ?[IGGHNwS -\x9s{`J:^B4?zf)#.!a1K2_[{lAnLL#6#. q͡S'[oGf(`{Jw$Ru:g<] 3dCp֓48óe2vw]L0Wk8h[, {}/l\_GH#6^'l#6 .rw 0~->]sws&e]]234ٗUt[mjp9-@Ab#6AklԕoTq4g3,Θw<v/ñ3#639ߊ\a 7g< I$ #%!|_1w痣6t3k'6e>1~&#+Uܿ}.ƳӦK7wMTD_Ur{RniU!ȸ>mi?]sy:#63P[~O.p?h^Uf| 7 DN&6iQ-9z-bw{K4d@uL:؀r}Ӷ8\4ݜ&dOxtՊ1oSt}FZ|iw\E^q.y߳#Fכcr 1J?yU`< z 9=(V#%ۻ#%Y]NIr+0Շ#%)aN^/X#$ 2OaA~eoa#%aNͿGη8$Ks{tJ^eQ(L̐#.!~=.gm iij]bM6E@v`waY<<;%3!.I353差N (냝6c9vD[4;ȘQT 4ƒR.aV&BկNhFz *QHj$fU"67U<yb ןb T#.ZdK9̑?TRq߯:YEzhO'}ߏϯ!hRg8_+v4xD]g#.ZgP};lȰIKlpHN#?bp~#%$!!#%ܕUl?zSl]~aĭɘk jk#6v]#.HIH{?;uM·YMQ[qɃ[N&unWx0籈p4-P\I7\܂Bǽ9t<#%<3#.2sk!(*]A+hX™?v)i0h~~[v[a,͐wMN0|<Д'Q{jW[ 9`MP (y9qEXs!GWQ-g4'io/R PkȎB;"ks_}5fcfS.냀2 `DSI0&b1"J5ih>fRHNs8 FeqWzd5օ!;x>eeB-!؝m0M."7Nb>}X3:N7[Saߦ#Hwo̳۴!;Z-"zbS #6ՒAApieTA @h=Bˌf?5q&}rd^W?k{K5,_{/+Yt1Q!%*d`ڟ@R9AF"ݷf۞Pw|}h {g =׷HER!A˴,?=ȪDy_7x<,+.ehX؉+VBG=݊V =R7.rT'(fhV7ʑsWВ&QN!MpqZ%dn;'%1LhUQ#6l}i "e<B|3<<tR`6&vv y8`pBDdkOVR\:+;#sLŀx rk.̇xԩ4gu"0!we*t5:&e%Ds.5Ĭ^r)g[b.;a3X8:^ӷ&s^wG$f>Ww}wλw̸Ŷ՚Y$6a74E#v8omfC'amH|M4!vk!VB: ]&@[1#.s3/MC4X#6E)n؝'a[\f@n|?_wݨϦYl2K?*#%a8xI׾=sArfA#6 GMo#.F*wQT`7F4kA#.20hXTZߴb =H<XoA#.!rqg#%[:ylTsT#q`Z͓^!|Bl=1#8έ$'`Q.%+&{zy5 my,T6$pd(Q13(%&ŤBlaHr#.cΝ<Bɤ޻[ZPXwouM̪Li]C0=Xu]$8d;u!@0PTaxofvfWl׀ GઈQp@MqG{t&Sl* z39=Ny"w'Uq5v/24@l'lȳw@'gyvkҢ d̈́4Υ|#65tM]<#6 i5{WB<ᤜ16; D`#6ɈN^DYuQXhgLm2xNKaʣR+q (X8LoEh.JhBHN_C]Y.u-,\@ +ǯ53,UeǙ2ffX12%X<cjl$0n&z 'qQ*WFz>7w&C›9#%!zlP?V[=s֙&w"TK=k9zpm)i֚3=FChYCS8G+}95Gsid%(#6nBihIwP%,h ^4{w+#6]nԍJ,!25.!bΒ[sHƶg6|kP奕e#.dӒCG)AkotcC~[^#%DBI(׼U6U;}= |#%֫լ7:UC#6S;\6FjU*F7`rtXȂ@v^G1)^4N)LvY`']L5m3}Y5B@- F&)l۵m,r,aH}3V%O2(J(Hk2fdqDy"N5۹]#.70 :06쪻BR$cHJ$tU&WD9ς*!b<B#.#.EFyhaE 38a-'/JZ*Vi4`hRq!]#6Ρ0Qe._B"ɥ#:+R*Fq=r@_9!/MHYLӑgq.t=v7]Xu&I(Cyj4K d;Ģڅ#.J$E1Φ1dk[&ᨡrGX!"|&4уIr`<4MkWg7&6nS./ްF2N;@Df⓻U%Nn_~L DHf |oRo85IF+:5_`d{uȦ\\g#_-7{"#YCi#.W7R!E*.I#6wעp$Nj$#.xy|FPLZ#.(̗nm&C#.F"뼡%z9jě60oHCǚ7L!3 0q#%;{[@o $VI6-p%A<O'PMu#%I|E>8qD-0Ƙ:=&-OgƠ]+<GHk%٢*>KM>i#.$w_ܟь$g\W3nWVGMOUy#.Y7#.ﷻ=h=BRg_t=V[(@2N. c !H>((Q67*=eHor.#6\HzyC?eBVK<`PA>B*!>y5T.(f(ε<Uv|Gzl!g"N`~aCD#6F [ۺ7n6R[ P!PE.x5 #.+i} 2AAw R@HA)Ң+4#6 fkك:oNq$$$byp$@( ާpm`<lr#.YW,$ ]CP;?8Dv#6#bB-h I{ĐB"@|\*X4GZA#.ʛÙx{i#.Pd ##%=A`|x_ CR4ԣ`$kB1K#6Xƨ1tIʝ{m|#%P580.HD[ꏒ{-ύ0~6#.ۜuHKȬ;+(0#6A3%_\$͕>&@!h!7#%#.[]쁸F/5"i^Udwn\q/ p<` b鷤5e6}cj墩i,AE`d;;` U4W٬c z9$u[}nS(G" uM#%²/lM` 3  IBŅܣPb 0*z*RIzM7Mv%^uy;%ewh䉽#.Eh% Ԧ>6|,}l@$~#%=$.4eEY $$g񝁸|o'&_xre{ڃq#/9xsLXDo7 zF%A\Gvg}hKmBx#6I!ybh*O{hV[sU>2f00$Yӑ·+?㐖#%OP?UHjgy诨f#. rk,CǕs(ldXT #.-T``0f( C.h_ <rXz.[9WlD~++yKD5ۭ=氇_uZIt i!,h~aC#.vߓ!@#.W8G䴮M[q'5cyΉ\sU1,;n)ȣDl{#6ERNϤ:oNEbz5RnJ\E k}Gp05+"Nz|JNqz$Mk/Hx8S{Qy"؛}+<1#k*h()de1*##{<#6B~0PPaVd6jƒlJV++-MZI[[YQ @AdD8ZUG]WHs"zdH2#% ug"V7Ձ6?e늣5 hXdi#.,_*i{W BI*@<&k(}HtM#.%hkI/ҲfIrw*(w*RZ(x՘N{up&ݨ$[j]8lJ*ʽmuA}C˕##%QM: *$g3tBE P'H!-Ej+ܦl)S(f#.EI)Oh!&8@#% HAdANUԣ>|zՋr7Hda&fA=/Qywv) #%H}0!!?o6*9}H $g/b5B^! i >ЌhFD"#誨d|M׽NV2]Q5J:4x(.QHMn]E>,xC6th/ɏ{hDh8_ē+A#.Zb i0"$qݻ1;nxIx5S2#.Ou *}9,1KE垭')#%B H싶0=I;B q@li8"d3!x#.AR:pxԁ$S'CߣslQap:rS^uz놳dT$֏` >D=N3%cxIʼiă=I#.-3}r#%4vvu#%NvnBq Y(lSiLc&b3Ox#.k#6P2C+LjAX9pqfzblS-&+XRka\rnj`X#% ⒢ ;xh}6u:1[w~ ?7(iHPjj4QPuQt?_bɱ*w(QaDM Ebyf:e8C8PX{il[I~#6!OLc욾@#9OVvøX79#@AV/5MP40#.Bgk7ÿ^ο}(=c~'!Dv^#6@ZXz}fT.R>ޙSڶ8cs(:>z ,>c1t*UGL c3Zo޵㥷;WecpqF;jt,٤m*a\|k/оhDP$S x7m"[lmp`h8x}7:דlAÐ|v#6w;60n1ljm>;Qcn$[9ڌ5)܋QaH$h8f{xl._+bsaצ#%YbdSdƁ#.ps{Il0R&A#6BHjPA)HTL3 #%!+RϢVAHJcv>8}h2|!#%;v+"x%c@x@ 8`ŶoS'+96*v񑭚l6ywP/fW #.W# dn;vFl[ w6%9MpZdK[?i`WXAԄ}{n#.eA=B: h#$R"!>|8f#.(SܾcC|6RGdL}=H)G&r 0zs:wd-㡧"$3!~ I$߿4!#( ] kl1v.ŽsϺ&]CjJm7ڴAh)(rVTu55#h~=Yk"N(DK%OԎLzJ"^q\ѕX0&aŐ?ճhFE5|eܼ_ '_&P5' ;`VSS *Us43d\I]Mښ[]TR5 0`X%JBi)dpnDpfݳ XVh55fʑE$MꝠ!ģc#6l1gk~cginu[P #6+dmJO}g#%#%C#6t|lv+" S)Dr 4I#gmSPp㲵maآumw`WvC0_#%bn*F[fUۦ<ސNHON'H٥#6#.meQFdQB$J`D*XR AiPbB>&'5mSaP#.3U_\ƃnXڀItAjrir%^[p!1XZڧTYfK`^B:"^%GP߭:D-nD\iK6!E$H#%;+:(gw'`v2OAڡbb$(cB6H &:[Bn6LY&ooagZ4S$.v}^Gwx?LD(:lڣ|^ys* oo{o)4"E֊"gьf:JM.8g8$Ie7!F9u VWTvgN^ׁt) c=#j7kgKm&ܹ5.Sɸk4 JApN:leƭm2L?]YءªCC<sLTCvA />!!hl5\ЙJ\ ץ dHd43d=BH#pK5Qz 5Zt%^EA@@Ȝ_e{> < w+s, "5#%BPiu+554o.#*H)BF`Ș`Yzɘj8M!KI} A#.``(3Uԍ&MlUj0%ԥ-_烞;^Q=\)I#6wH`|FmA;8ck{ {44 Jܙπ}LPCyBMdYbyOĒ䵒~ɹ#.C"I<;Ջ{Finl~}|B{F d#6!!1134ct'HC-&#6hvWqDAt@QD [! Rs't-T#%g雕*YKtF"(UV#60&(&T0}zOQj#%Nn(VMBs bLcC@!.t1'?]& i(DyZ*BejMdFFtHF}Z0"'?h;dYC*;C PCt_#%2NdH<%qHMI\u ΠLĦpM<#.8%fLQ&ј\*01xaj;{<(pю =Hy!wD0ǕAc&b.5sSI1j2EH,k=2E (ʽ :9ZkO<שMF!Lc`03s@}YN#6late<p0J"DzyCr6#%=l+j0 jO|f>-=q:¿ע1 ^P\aaOr@^u4} xc4uXgds;",_MltKV7Lš8 9T(5TH p_;KeYJ9-UAXI.#.#b lImUdA)jѹMF4mj!6I&̓&1>!m.[R>]]+.]xjaJ-#.#6%dAafȭ[&fVI.۹9ML5##|[Au_kA:QB3e/F ws+-+p.QS&h%mKO֨fö Ҋ:yG`$F"|hP;#.aH4K_k@ip[66y3xpBFfL[6knk׫TݥʗN 2I+Fmk>)4+.b$馬˶?mS>^O㍳*0h'~;j0պ5-NeF9HGIr,=4!{n8n ~:ggЃ`-@T/?D/اB !DDE%#.&3R60ܠu*:h$M4h@I&M/WX(,fh#6l#%S`X nyzڹiRJkVr2L7krEERJɷwXU :c7weڊ(ALoܝX-=1&XL9! }IǾ\y$WG~E4mS_ G#%svYBog?~A%*jڹۈ,#6ѵTjն5mͶ-V6vC.'Q E5bG9;#.Y4(u;kۜ+4ֿD4$S&e3J,)I)5EAΤZ1Q4ԡ"P~ܡZLY%[4)4̑hEQCMDFY)PT%ңELFhL)T&%!MAALcH`4S&ge/#6b|[|X&A~lU_SBݘf̈;708Y!,mfvs9` 8#.4czKj\ 0]iټ (5zmn.֛,mDs0xxN;Z-_K# }YJһ0te^fw~A{*񌎃bm d Z^o_bc5#fV*(i`!Fa><90ݽX #6"tXa3ٟyNUMaYb†+NbC 4}0_[/cyOW}#k#6xn4O3=GQP+|ɠV_fw&}z^ p{"r>MG]eUY#.q E)AT$/rdǥoK}eG2XiK􈣬%x4mcǗ$I*#18--ߣ8#+gǏd C֓Im{N`g"PREΨ)xcB?+#6cguVˬ!EPӊ#6+G0'#66[G8 pI5,nm_&ɂ2Wc.["^7L<1YCu-4'ѯ$41 g OS䇽^RA虚O?M" CqdNIDBxK'٪ յގgd6E)Fm2Ib=Oi,(5X@\{w7g FHC:ڷש']z vBx}I\wvϷӖ_? 畧ЪX@w ߜ5#6)~.wzD nGkBkjl8gm]%}; f.IG^fPs3 j5'B;Cq #6;뀛<r &\?#EC8(p+x#.j_N9ph>?/>50Y7Q6ȣ0Ħa iFELŒu530W>ًө<4̵<3t,ņq4hQSRJb5D(EVL›WUޜŵ (%?Kľ$.1^{S{eL:"a *cuo\ a\MLhzIƴlV#6'S;ٌ;WGv8 acg9t!^ Ѵd5+6 5#.,0&bNJ:~g]`ɐ/,7#6R[&*_4To#.?02!n6#.ΩсĐ4&:JF9]Ѥ]Ҭuay:0j2w67LL$g}n@upB텴ց=,Y?9@íRPɬ@ZlAs;oNqbg6fl-&<F!OB|VvDDr/,ڋz"VnfrgSqH·:Ӻ6=V_-eǮƱmOt)0ej=1F1WVգi7)Rw_<0^Pd>llg:5%GQa3:;1d9LJn5͚']ߗQt\P##.'yJX5\Fָ2L/F.ZX]G(jS }5#6jVͶ]*XTqdFSBc\ll^s9*N띡,ߞ]̕O,K+Thu(v[=eƙCAib uS"q0V^ ZkkO+gX]Ne6_y#nukct9)Y69Znl8\e#A#M|fS*CP7vBEVU;"֓X.\[DY\('>;8ghh:yYz&YNڋ0,iQ!XlhpNVNځ]#. H.q:RIFȡb<Pԙ)>4=#6YE&)+a0[3A@bXQ*1X1qIN~.2ߩh7)nepOVRQ9B&P7sinc7ۃuU6vublwsaevfcy59ݬe|[u`z ۙ L>8O/ R=:%)ǒ:{ZQF72f2"t[LLLb|ZFcE8S7GQgrܨ;翩1W i#\Z%pUX6s2B9H4q:jn21 O)&PmV\[lf5,yULb0vB&ҪCr# SH^i!ʻI>v8Y٪WaEZNχ+a'gj0U☁rӊ*L#6$ 3v{"aN+(0e#6Bu R`hÀp PI,#. fF#6ڰڸcM_{Rƣe\1f2R;Zaf%ñb"zͷ9V2#.#@DEuBI@as#]hP h}uz1U tͮNzzOQ(TwqhR`pZPL;7CN]"̎̀#6.s׫ZÒ4' ^le,Tmt 쉌SR 3 @0ty\)&#.:b8!K@]Nc4܂@re53Al2ȈbiA-RrvֺS%#6# i"23SdtvNI-"S#.['2`Rqf d5eR#.XsL%qiL§n2c*<.G (Ncim T&pL*[#%X*tvmB $I3/ObYDw\#.dx/#.Ӊ\l#6e47U3}/lpNZ)jx#uMXNRҬΠ|`5`p3Pf8&7Pn°j;'CS_&ӌPiI+]#%OD,ze̲9i7R#6tS0@eusǁM׉#6K5BE !UAI HX.b004ڐb;G.NGIF1T*N_;QwWm3oA8TX$GaJtކ`g N }[aYIB5R[#%l8q+Cȡ#.F+2fis l3YHu#.Iawmw#6naqʌW;eU 88JD61C#%CGf1g$NɆ#.f i q0#. ( hLJII4dpjri3f̺Eِ9Fm0jJG8gt^Rg~V@.uXil4I%lK900[Xn&uA#%0IF %#6qC$R5@w;iSh#┈1A6*?g|z(W*I"H#$d#%|j?3$䈌@q::7HhuF@&4TC8 #%-=k<~X$*vʪL>Jhi/'س7RM߬YEr,;B_}P4&A\s'M.iRk5Bh7n20+a7u% exnX!Ս#%c=^4pu\0R pBev#6h"Jk[m&Rj*ᚈPRqX1H_D#.o=#6JABp5㊡\C$;8SoW4U*#%nnR 6._SWwUٍn.nn-sd5<Fȵ*}A<`PLv*r8#6#%|(u/qz0g-Z" ({'@7>GԻo?62.0V<?|~ r=FwEM 44,;Si#67Pon Fbh1D) S@n0.֔.eA2Aczf$YЂQ#.cG3!:?+x˖%A.9FKP?V?_Ui&RkFQ[FIDL`3-lX|Z0POҴTR᳧zK:k5vz4D+Z"kDFI0#%D #EV-ރ xfPD`@[΋I3_k̹< Պ~Qb*Hj}Ja_x;w$l[^A4Ldpb7N(cKi"F'f5j]*MKoB5L1fR@l@E`+KD#0QR&lc8B+ Hy˱JEHq @QUo#.;eK'(z#.sdd%$Ϗ&ٻ#.PԅHT]c(iEj#.^&АZHp08W.cc#%m LNLuY^sŲa#%=wKbmlc+A)aLDH -5Iq0]i> k@ /_m!*  I, ݏ;dD_&,ŠúMʊ6 {8Q`oUNJ## qe]gRrE0;>/#:>mcz"4U#. 2]l0jiiZ}kw 3FCzi!Kbg:'5bCVmc{^ZH8 ˿+#%ӾZ AI4z^ym'Gi9E$P%* UeYNB1B)#669sypY.ݘUfylMxkDilʋ]ѠC|F1#6h7nbC#%ktE.J ԘDbTJ Q$أJ#.L@c#%-P$PR[ t`luqlgL #6?@ B<5 ;+>ER+ɟzM8Qb{(=G+t ~L>WT/k <1GA|A]fs10#!G"dFH"bƨɶZM "BC5 ?=xT\KJ [ikE/k|tu+W n.rr$E#.{:iY%leʵS0:2KYk8ijm&&dv5-zbScA##6pH.KӃƶ>-IS1d<SKۦ3ے$iȉ3̸jMsy@#Dmã(ьjuN&Hmr=fWN5凍˭i>S)1jnI$C#:y>*iyXzMp\J}e)3&=&B"75+cdrpQ M=W0 arr1@tXEDLX%ѵeB̴˕kU\^y^ƺi ,sKB4!_hRM:#RD͔Dkm^| U"hI1<jY*dK 퍃0]#6j&nu&\ɖ*lLi[c]hTe?HÅ(ѳRme-U$f5S5ԵҴ5SUF6^xME *fVЕl҄T7ytccQթ`IPQ6@bQQPX!+D='qz.kWaK9S@6yO;ngq Oz,EqhKS-#%DFOXť*jXɪm'O#;LMl1JS "[KRU(J01!UЊa#%j#%)"n͸esf:fsp#%H5.Gf` #6 2މeRDT5yk#.#.=A4\Twm?2sO^nU \#%ruJo%ŀ AS(L!$yB`w?6lx3ݟbL8o\rEDS,`&Q7ht{+on/1~bx#%.jޡ ~0Tƹlod8YG|fQ0*mʂ< h%j6E(eQMmr,kMc)m&6-")Y,JU,g]aM)l1&FڦMӊ+"+ }^ݻ*b(2#6em)IkjhƥWƯ~l-cdHen]iheRMKmMl3#6ilMKlcn͖;R2hkRW5xؔĪFk[L7LUqTǎR,^?/>w>Bms'`Oe3$SF<XqIFzTX@񘅪m_NW{<{1MEC=#%.b3#E $T B 6أ^S "-J٦m鶱oK5soה#6QdFAR*fSJl)iJ),E֖FR3E E&KmiiY5Қ)R|V (-EeQ-%(JHQ)M"jLѰ1ba+e2IdhXԚj#6RT Y#jQ#.M !BJLX)2 IJjƨ$mE,Lړ$6֥ɓFқlZkVҲMԆM6)Vf6{*zV-RmFդJ*mmZc^6g]! n5x{5`fpbz7WkN-WmE<!#%yFoDz$>Xq⢵E^\$1ga>kg]7b٠3mez4:.L@ݤ"+Ļ/7_K3=n9=C#6?qő@P \cha! )]28Ƙ (W_@p$ -΄>lsu)P;+:lP .3уILFz%;8mfЫԬov.yg PM"~}b\2e؁WC|sл\Lu#68TT>KtJZ~VPv Hdְ# = s~OlqGF#6ƺj76Wi0#._}/[onӀ @#%#%ljʲN"Q@R+r'8#6o("2! Dxbu^U=_v҅Xd[&ya&\TCA!$*c#. SL`OI.!&*xVz@]7=v#.W]{|)LcܚP fsD\Ox#6((S)b#RdA] %@gaZeF6HF&cD2L#l#6h{C5U.m H)fu'6ơXVFA4^Zj vg6& b SRF[K#6@QI&#6L*cR⑿JT#.t#%6:^tqs(Q jQhXzA;Pl|߶4" JLhJX`8ŤM5k"k;CXxj/z/#P|4L`Zh-AXAKۭ[E9b$>#%0 =V.nkm>s gźSi{HԷt0Jc*$L¼Z$!$wI;tnl -i!I#.njRYVTʁ[lc0XlA4(F/nOdʃte&YA16a2e8Qv!h+br@Å/))iQO9&B>|ɭp*YKmji!:I$mDVCCpPc&P1ssgٙ .!H #6PR3xQȵxǙcQ:Ts hi5JKF'3GÜysKM:]` 2UKՕJ̫ZN7~R(: @"ȡQffđiDThA~~N>TPQpGǚp󠥀CaFE<Q#%,2䰮G&9Ť;1AuȌh)`kND =#.Áq [tcv#.iLjD.1V&q"J5`̆/:lF#6ROQz#%=;No+fy*wЙF|"TXBSM4DB *rJZeE@rfP\/Q#%(tWx.ǨP QՂBUN#("X2#%LFM`'fT(#.2p.=Zpv3{raY &&4G] :\cl,u/ xanG$46PDkIt2v+,u<xϹem "#%$l`1R{nz3D2ә#.Şd߆nh/!ݭ]n-:+~A8uN35P pCm$/R4,í1=;Pp'#.> UGLc0u{2=A+('_HCX=DmqzEn$ON-ˡe#6SY}Y#6I %ݣH?]$E/]j˼nnșHNZj%mjRC@mQc^:wn) 1K)y#6QҐ4l߷w7D9;nբꙏtN(5IrB̩[q_SRgs%_ƗM~%Db+)'hwJM05YFqt "懲:ʭ4ѴLZQM4jW]6rbK$ج[FߝVbVkJ*K-k嬳oki[V*rtt7ĸe h 'kг)K ]e"JJPB7&r2(-o)<( 7 uh%,3al! hv>3]l4}X(qOtT5(i(#8+!iH@aS*VzPT*dWfTp9P5tQꪅE#5v"IUl#%&+t00pF`&,܏}Cpt,VpCA2 o|'Pg=b,q B=cik=A~x#6gLqˀ.H\h]I1#%%扨&?dI~;#.a aQhFpA24 Mj-F?=+Rl~C~$4K^n&߃By(`=4x(C~A@TTвa81#6{B|<Lu߻/6;pt6STSbGzCw.;=#%d#%RI #6J*#6DYX@PPflY!xEdBD b[3\0_aˏq+bIbNQ~0Gq9eSQ~j DP$SҞԣCN/PlV_UAd: C[!ak%E IH^EWc|s RgńX!`≫r'P;OybPWG>'`y~6ߝ=a,rtԟHT̉I9VTSRT IPцj,TU#63SE,#6@I#6e,%ZeC@Q>9y_-6#Dca]<>Zcm\g:F0 0#B &DeıGf4'F4҄qIlb-LQX(J*Ef-H#.Ԓa4J@f@ I2Pْbb$]LR`MS$ ZLibm-B1+=B`HVZMF`L%mV>L䱼xaEXVÆު AF4gJZ,jj0'6Ѵ\6rܾ#.c^.4<-14\3lApjӴK&D`:3k0,$iKbc,PyA5&ΨhQ.H5@k[R5O<#.C:Q6%e{]N! uP2h|imO:4Mix,#>c!Imwe7Y q1 +p8#.ߣ.H( MT?9 0`1 8A%Q#FljTj,TIn0Qr]JƬQmDbd@b -eAn*Z$A$TɸC )ȰD@QHXHEǼ?X#.Gꂤ@Yg¬{S(kf!W]n[erZO¡.TPI&#uV1 Ng["ALlBPoTBDWUZP4mdJT淍V̈+XlTzŊ껪-zjב/]n\|ךZƯ7uY$թ3JA@G2<㥩X|!kkE{\5pʑE.I#%}3@ ,e rIcQ4״0NK2#.#% 0#q MnXq[l'}@a%ǿ3š74&iG<!DWbUTU˛[H=TvU hZ7aqP׈0a̰oˎu> ECObȀ(|Md=` T6qr.;Q$!a^ ҥ#.-TiKJTcj׼Uk)WQE%D/9@5];c{nJ*dBda6'.vFqpFCN3BKQ4]#6Ӌ#.g8bCְ :P&&42iPZ0#%G{" M18=ɉG`‡+h}*M b]OCXdI"BMQQ<nF^Ϻ!}z`ûdW#" %54I7L03'|@YA#6?KmɃ:Mlp(̓V "ԧVl@,jnF@$+RM6SHDB0 D#PN5X4O4D1͗='155{:eQi`K;41$еcF^#=ΤA>iTSMZʸk2P|T5PajJLi֌e5tA&2jܶe-,`LKll6Lm$J"3f4M5MUQli$1H$#.GiGL"8lƉ)D MVVŮkvo^aQoѴצA'"vC#%#%FȍD*TK[R]KUm~B #K=Q_fHEQ GZ#>adzApQٸhB9!OGk!' %J؁!NHl`G>B zAm#%܂%KtDByxq;8<I#% CPŌГyzԴ!7$=6*a x2^30xp#6XXPȅp>WU8;g&Lx;Q7VZ֐K6x#6ءp[1v*@7̨ݣB8#. \;:hzp!a#.DV#mbL(4t!#6IO;NϔRb.-+?VF0V~}мmFiAQ:ĕ#%>#&!-ctL!/ٯ9vWԐr[ 9+S:~rt&-twL ,S4]P云C#6(,HQXP9x諒#%2O&ѬH8ՖY!D14P)#.h4L*v#6HLMIJB#.X11"lN9anTGd#6숒6%  `6Ĵwc&i︷lC#.@6dZHfhU)%E`҉c JO'T*cmhF&(}q#%DB;sR; STyzg?~<s/T #.\/l:o\qF(P8' J*ƛ%S -l;wDÑ@P@4B PEBȔPe'#6zxvi=>~ Qu}&`]|N^@UBfUЪ`EZ}MI#GG$=;}%&Zi]4ff٫ECnBvP`Cqh\5&+&WsLjF|fw{rTP#6`R#.!%r)(#6a<,<[މK:Md.!Y;0UQTY"$D0BR0!D.bo#]̚T`ѱ".)drf5"^ ih%|#6eOuBȩPW.T[#.i?L#%Ez#636~*(4ͻCVn=辻VHfmڨaYe֔ je 5VWE[$FBNy3, /Xp 8gdp:>j"O=HEeh ˋgؤHޒ%?^uFVmTSĺ+njB#69Br,81rsU:bkg:܋|Ʋ:#6K%$,`#%*#%A DPȊҥh#I n1l+p<;T) ٿ:Uyne#6eǷVQ12Q#%dd!CBbuE!Gv#6($U`ѽsqKѣv\5 H%A#%Ν|:LOBŜN#64/ LdMr5wmJIǒBHT*F2D#6 _J(-!n֐wrPl W1`Gnb bFϸ:64kR&'&{_)wV7t\-25bB<* \KlH2Ym@&"-Pf{n{G.z&#.zP1`WP2O!-0ȔfFD$ #fK0#.`mZhmmؕX"bͲ9Pí( XmG0.M쀿˜iLI!1b;EP DSqScvw'祥U<zNu 'IfbWGq'NP!#%ĐIm^efʍdj)}ʾLU?OǗkە氵0}ސ^xa6<XJ.mڦv"OmՐ}Pr)N=\wˈ(ײ|J+T-`U vI{!WoUx$UYYya<n6I6= q.`vǁ]to +M`sZQ4$n@tp&/mHF1מw߸,bB&3k&72^A#%4߹LLK3Jj >E/A!~,v7ۯ#sz%2tƶBÊ -*F,"s^C^GUG݂!p6m=m#6N0^b!d%]-.JZʝ0O ^!0ןb-sy䃉eSZXKfsM%8Tb\?}{>%a8qgd;1C ) T'd%Bnp>f>yKp#GsmGycQۚ%\U#.$HH0QKACoUУR*s$.?K b\ňaY2J2TX (Vy۹uުZۦ#.1AA( ^3@dntyr=EI&01$ե["'4|lMGQOgC_4wA0CfeEpIGG$끪UtZ0hk>1"՗,W%F;q56z{@@q(yCScp_{J!0:t4QZ$\v庺ͩNݵu&4\^.^]Vl@ #%C@US_Bڔ5)ALjrS9 5TXlzMu>#.3k# j G#. 0nJYUiK:ap<h6`g5!50zp՝H!upS-VMdmL܆6u|#. @{&}vuE'sj>Z2nǁ׬-Wß #%`+ގɬ'm'#.|_uTF#65*oX~\gi[4Gk]9*itQ0#6'wgo`!(RY} _b.5(,DPRSX%DA&?3Y{Z$*mk_z.#mlAA[v,a$v+'C-H4(an~#.šX>7PţFsEӣp&K~iKn<{6gTSE_ϞRI'E,#.mQC^[#6<PiqL =Lo#%{BhҬ܁ls-jg0xAӥgLeXdb`Y)0(ϐQ3I!aÌ:ТIe)Ղ<=c),[I _3r^B8i5V-WBCGcHgMard7O1U;f,1!k q.aP0OvѤg NJO5Ĩwy rm|#%#%p+UC'G j&˖ ![#%BOE+QZ5mdƩ5Y,mb @*TEM25Wn\4"Ԗ* ARUz,.`!i p:6vg7nY;d(])5=:C7-\n}cuWC2mFm CEli 2dS ~(Oy!wF?"5j<CiRPl.@#6%5U=Ѯ%O?#6\p`v㋜NJɆ@;$s]_2BPV⢁0`Cm79FD7|Y&V,[}]t(L:"W.{@,ϬGo (TJs I/#6yfx&qK*.q<L%#6eq}Kw4(#H *EZ#66-5W`[@DFq#. ̪MZPwg{7>Gcc05⻝vo1kp&|OYQ>#.zޓŇ'kjaАTʥl$ Bm=eYb]ع`#%.L?ɧtnu#%N؀L81zp#.nʪKqt/B+5֍kYf2UBCYI MIǵ܄$:¤*uηLw]#jffPh]u<QfnBhTci0x`hpTZJ0`28hJ!ѭS LkLDa6(߃pHW֙%Do!HHIjUM̋6J\23Y2 rdŠ#6&ۤ 3PE ZH Z@ 0V52lfS$e6,3$e-/]˵ri˛rAf[sNݽy"hR3wٛ f輸"FJ2وA\h@4Ecf16#.j*cq0UkH[yIQMҁp&b\3 Pֆ4ս1ΙEnՊzEFI[#6<qTUu5!(db吴 #k7 +6\]\Y"i1 y ic$Ii%ehCVc"sz(T&0D1~/aen)ᙐi3aCwc<j[J\Mb0s.#.֤2E5`il(AGMٷ2ATnܞ]DO3JiAӫ_ݻ8+skh gjF V4.#%cKlTMX,A(SXzG 2#F4Äf#.nd|Pk#zL1fݗd6ro?v:WGƎ.5FcmtWǰ1qƌM!jMVoz5 dvUjhٵ#.\"9lT=C#-q}&0#.7BB]&$,eeUc GO8a) P4/t֛{5E]5fT@& 6:  `"0lU,P]DQ %Ħ tt=An n,8Eiqu#%T#.Pqbe4~(Hg޷f='Oq6y&V?狪+lMHCޱ\#.h#3mK#{{Nˏyj=y\M\lّK*̵Jpuj-kٻqFb!$-ۓټ讹ۄF6q8hPh,C7ふCdqm#MBy|3y!7稺(4QX0w,h#6?!\o/gg^ij#.M,=6ͪ뚷곐xFpS4쨉F,~Jֱr+id_>M#6kfl#6놡){={v R $T@# *%l#%H*GBXtL,:jU(lF `][Iwbםr髵6r%HTwUڱZ+U[[I@%rء#6BKI`W(06nnaI}JX9*ae(Q#P1EZ.pRշӛ͗Zr%&X %Os}2PBTBeER_QMZf&iE5E6+5IYLɉTDmLɲlklj79? >ߺDEOjM+ RR9⟋^lxe˟EӸz8ӝaT9h !M6Ѿ7ѽ]BfV#6O F6ӻݤKnk5]+ZVvj(JEHf;YȌIb2_l($D@ :o',PBM4p#6a#.gWPm+4 Nκkfֶ!4>>k6wĐ 0Ϻ訅]hʈ,`O]3]7Lf7?,lc]̓MBbA1H4id8w&N)Ĕv#.u?! 51<t2btV8U;"ǑC}tȎ|w^]3a8f=6̈́%~ѶzW\=XZha(ƙm<HUvЬbEȝ#6N۱=L~+"e)~V<0T&*ž2uJکXBxQG`h -TѪKo/ZB2$"c\I\rh{:UlmڹU#U#%iy?3vLJ$e#%b ZTy/o\.@ں[%]-lfmBl@E1#DBR8$j\0zF [oZ[[fIlm\X>*˼UDF>wWْKh*7j*A#6V(1*AXU!R#.aS#6$DQ#6#6,ʎXIvUDdА%0zh RLcAAT#.9?ϝ*(hy]asa`sp%Xgՙ3n#.#.H9F1KQn mkU~[` #.;@@HnݡQ!*'[Cm/C ;&ƽqj#%AD^G9z*J#.,Xc#6+fz#%fθC8>yvo֢JF鍔8ꦈbCD#%E7G*% t4n#61*M63qM!!ȵW|TaX## hLCZxbi(6t\YlEX#%$p4b9&K`Ȱy~5D_ #%^3;HנXYU["zGEMpCݑц|JNqI.k6#.?XC߶&UMWJ(]eB%\;(BLgޭWNWu\vegd!Q Bt?g> YA h7vgy\2嫆uf"i$l&ʖ۴&S60xMܮU͍w^vo.ul ۤW/9y<urk2Ƈnjh宛VKhԥη),'5wft$RќʻeγQhX"Dhm #6XTbzvA؉CH#%mo;T<8#%G; #P A;XalEx*oNx{SpAJA!E #%w'+}uh%z3#6ܟm:r#%r"1 VkZ+kg #6#%Ξ6S!5jjХ5{|1PA!%AbBVjuԲc#.ݵZc^li#. 5w!,8*N߉D l&PtIi&I#.֭|\{U۽(DH"^b#6#.)k`ETb"M(0'n\)OVu#6'=ؠ1HAcQ Aj=a3D#%;" T͖iJfM!/Y@sV2#ͩZdQڅD&1#.@ ˆHH"Zf0mFkBkmˆ7Lvvk_T,T $6#%DOPp%*CrW6}w~|d`B2gC#%aaHX4)RkUMfOv`iΌlDHI'2'sђy?b@coS5cX^-pqZ#%#.IY%iQ8$M$4B£Q!.pK#.dmsWI*^&ŭ*6VjBTBW@#6#.c0LK1B-$ok#% "JM!hKE̩#~N͊/8o:f$^쪰pք 331(`mx]O,).d0L\Ҳ u"b0h BXT+"7* H4u͒it52LURnݸ6 ht#w2H`*{Ntp~mfEDAى`=aPP&iPE*#6E#KT-ѺZƞ^v0lađ$b St\vݪʭ[.꾻mR׺؍+fMĔf#.-RHE9xOd#.yþg2>?D[a3FAR1m6q#.rPqP4`!FDm6ɒc$cvHEJG+2ĹR&.-S9;73P-2}ӒxPצ!DO[m4;CAA?{@~Fʈ;g#. ##6&dT#%O]#.0UX%H+Q#6'dg";#%h#%6ӆ#.!]zLɿ$уHX*fO'"[>PA#%GQMA={}ᧃvY)uA_dFϲc[%oE,y J2&ot;6^ouA(77) YLxl+ۚS54w$;8SVT:<t(HȪ',RڀRgpoɍ2"ZB0/x~TCIh&"#6bC;3ْ>Wc~ q'lc0U>L#8US3Hw B?kz&)Em[p!oqJЗY{L7LAӎ V0>BG.#DDJ )M+Ik=i4bs '4>}2ksH|DaXwk}e&fRyiΔK=iٰ y/u+`x$0}]J3d-^϶9@Ѽ#.3]7@XT՝F{벛Q2fqLd!o!t8<6c8CgB~QMKCm"9n H<J#ر#%׋1Mn xaOڟNuQbz` 74*9Nt Szօ8 zJ;ExW3Cd6JH a`&C! <,]IZ:}P#6f27T6EnmFؙᝎOumoٜSV#6ԒI&#}3lCY]6dj s݅M)gV\,'r8PC5U4v!yc:pkd^קD~ ??[TEw;kW]!Aprmd+ #L` | P Hƹ_gXo,i\ Xy)4`8kgBCHNw3Uv0/n83'=1Mwə1}2xERPL@עLQRI5=x_ZC#.eGke(҄ F@ٹuWn q/(o`ĂHI/#% $;H| !8$DYDvtFX*G^ҙ @E8*m81P}f>ޝs#.An"3-GCt.9#:>pwP +m&#%fFִd92hş$Ey#6,:@(:xwu#66쯟Tή>H{:rzY5z[(g 8uOڨ 0V#% FB$<jOfD+8Ə#%A)hB+ qk_g??̬OS/P@JFQ?2:߷gdH5R#;M羄#%x N_n1lX+ g_4k3>Vgl6өCqEW9v@яaN7 vV ?T'e;}`9=A+/nwBv>U;QUΆRt:`#A'B1a+1MpAH!OmmCP6*#r=??KTH?U7IȗۗXrxi7ƚ:t^ͣ!K,8Azw(I hQw,.1Zwb!&t4!.2E)OkR4?eBF1e$8xnnx-PɌ^û,XFKP<+wOjѬEfMRbZRMR(gfsʵ$u.kOU;HEUԫ@ȥ~ש?~dmeI0hmLBLhb#M*FJ&!&͓a#6$gxɳ':7ܛo'6yRMv̠0h4M4aK7tzh%*M^&y}G1Ԧ׃M6`m8@{*E#R5sQ틓ގ&D +fCx&cz\/ӆ[]4>nܟh#.L 9obkMNu.j!2̺ju^83f[G _3O8 m𦶰9T/ 3#6Rim#%F"[miG#%0NPȩ-D#61u2HQU;JnVݮsuukYۉ׷7 +l4h@1A*%*@’&M˘#%_2HAB,$֙Wrrsk$3*Fד2LE:UA-HK29XarReF?6tees4~/c hX!#6Tr!S/:kz)\Q!T`U7*͈ll/c\(!6B5A;ͷr.xǮuQQiIħ%4Ъ!h&M9Q%SML<D$AVeCbz*:R\+(mUš:‰WT7"B\+-u66Rʖ Uj<«5C`5&nnU.*.TZ%&#.*AѩŶeތ1'39Da[#.iHH7SlYeUƫA@; )ucN }f=,$|WoAR<&QAg#‹\\Cz!GK1dR0qy8B ټ1EMjJ#.BmKH'pJ*8p#0oŁ-ZN5FA4B戠2+j!B SR1F/40#.bR sȱTnͯ7nnXʾNL!ILsC#&DVE Fi(a S#6H-2@aZM'"1Aƍ<j˧lUM8KAء)"Z9fJ6ޘќ d'̓4ǸjN0fii(0bQHD5qen#.nO\m*%oC#.ʌMu…wjT&Cr@ޱp6z$&p@V[iYD(F)96pҡ& 4J(0\0 (!BQ!bJ"4 ՃGMI)Ǹ9pd!U![;+QbQHCTEI;ڋmͦl"jC`.3#64J("#6H==Rc.e}>RDFY>REB#%!TAܑB{T}*=U&00ҠW!P':Dg<F rO#.4P쬐p2Fn[{k;[U0Ir Yvk^(RLdRuB滳qP(f3Т#omB7\㌊L6-"-۹Vt$YIkX$Yjt/vA>#.H1G۫DPV( NLar($ВwRb4w^׻C/X,^bK\ٽ-!lMv|!Ȧ7@_]̏u(7#%˷I#;}+?}n*>[{oѤvB ~?c(G U#6+杂d礇콦zR'j[br#.v#%ηUT71wVfmEßX J^v# ECyƱ, A2AETNRQeTމr\95j#.&t0cZb8z0mEfHMq4 9 #.UHr\vV^QkrKhKrK\6UƎnV鵷MY#.cnm&k-͝r,mj Q+;-#.w˻$v07<}Zkzo=H^"΁!7@A"#6ޔ=D v_oQy>)G#6"4=$S2w@d#% Gq1(IBH%J*N^T#6Z\#x b*""([u#.mt~{oFRd%T] *Q@QHѶ%[~k!D,#6B?@#%$Cϥ'HjMFF{ZPuPP#%;f_@OA$;&Ԭu`٨3`:H#"&Q)T(uqe-L͒IJ1idi66hmIEbRRJd4)E*kjZVjZ52b֋ci5[Ϻϝn$cu#.Ǎ"6W"Dne6aPA$rtӣҮ=t<iqDO c!&$*+hlc H-a"#6@`i#.H 4ECJP*(B*Z0$(B #6Y2?5#% D% %ݘ:WV(@d$f2@M%^B3`N"vmwcٔQRN {ABH,OVȌ5Oȡf2vpW_[,1Z%/-l@ҨܨMxFߗj1pV!TH!PJu0UR8GaܜNFnɼ˩R/]sQhZp9v?HEozp2#6e_j`8C+dX{Ap!>(YD˳eJ[LTzF"``΃ՁRՠiJ%MYS||#6uֽusFp"}rC"lʪ<oבMr# X}vt;?;#'Ri@ܡM|]?ί^B}#.#%`A/ў|hl\1**;3*T4~3a)GZ*zUƶ` p]ڣ]u\ʭJQU3&eI#J{i'[lǿo5٥'Q#%3!O"eBGVH#'53Z#.PRn tCxmUy/VA<(}Z81K$=E4Bl:ESlW_׹/#.ۇQu:vG\{KX{V#%L#6Bk7(/%ÞԦb-ĢFix$ZF;vq!g7tVBZS/ yo#߫%&sX*77/cMrW^3$"Q?:y6"ƺ{#.V~ggsmm9)jWMmf 虘8k[]6rXNVRTYZ;grsByo,pѾa& q<jf)nۛEŗf5w V\)2l`qDxf^aTkĆWnh6m<py_Z>ؗ[hNI/$f7)֟Gb$CÌCC:s/}wʀDJnTQjPxbZ:>춖9W_Jװyixuw}:甼'u{a*^WW0o򁹘8Vx˜|ʼntCVo#6Uvga*F򪋭lg/=5ln.b</Deó՝<s>NΠlrA׬<Ec!"D^y8ܣB6Kۭ8,g :dDLoY0Pڌ/iys d)7T&hdr#6%#.-/C&d.]'O8m7UI,`W28N$`.X@=ZmRmf3g#RcQb3x00_$gBț0ں9#6) 'ϕǖ ke yc0)Հt( dJݧ-AǓ0<^ G& @1[cUNcAn>Рo#.(#LC#%@: #6`FaPa:L6 x`v`qG=269kSAC#%6s^zv Z3K3.&˘sx\#6?#6_Dd\PG`{uDO.O'6 [k,* Gg ¾T"Vs ##lNƹkFaCghz:癎>cETQSSNFb((SۜM e{A]k]zV4o{#.U*#%9G֓6(#.o϶;3\4G>FD(ѽp9f7Z0C㒚֋N21D!.e-lZ'UbQء~%@viVkQ-29ys83D^[)Xɚ" Dv:e;F#6~ٸwÌWoq̋';Cs:#.a :YT [4v4nsx; 2~sw~/j<\WtmG^5&ow7 A%#0M8pq<~7Sۻ߃LΥmZ\dQ{x09hRĥ#%}D4bͅ3璚E=;H>.x[CUZ=7,htz=<7~OfBEo79qLB6Y܉Y;W \q.6 )sf\=#ՃV4(v<@ǔ,,g (y zw-H]BEq3:f,ҭ'! @1HF vM\; 3@.g$$VtIm-SL0w0'#%Y"aU]C#.`f+um?NCw"0)qOn"xXN&y~(p`\r P <߂(;VL{U#.>xKf w'y;`kphSTCç2gϥ_VGG8v'$ODMN};O[Tb,ҡE~ eDamlsԸ#I#6K0m&@<ƅ(`YF91!"DȢEXB!$Jս+F5nZZ"(ciFPTҊaQFR0!\g&x؅ @L##vЅ$c9bmnEɱ*mVUQUUUײ܌P:~C5]c-AKx*1K+ud/io #.hHicf-+W SkN4bJ:`@,זcɠ(2tHqbePDiOR;]xa%dH!V7V (&icRE,%Ecbi4CZ*=[gAuwLtd>lL!P9l#6@Y0 fL7zU3RXQ6֥4ێvFjhMUzmkZALk.<c9eoL$DMqP\`SZXL*ccix12BC$Υվ59;haY SJ!#6A,TP2cLhǻ*sPAnU"^Ck*V%+XIw&]J&0ghnothsiHN'ai]P \R8F0Tj?޺֓cS򣜵.CZFLdq"7*#6* Ѹ0m0m15+Fn-,za[ tC 2CdT]&09EZ*#.hFemF?S5@LRg%|4k h~Ɗ$*7Į@zi<D]8& .r#.VFq#BZYmrm2L䭄lbWfJڢ&n\cFV=^1QimX9Vޟފ=Gye}#611("фe0Qs zXr8cMA@=6alS!vH#.0Ռ 0\d#3$d9:WbTFMP1#BCV#6e-ͳZ"ۭhDF b0f8҆±Fx7ԃ{ A6&'"ː9#.+UN!U*2s1M`C!0rc$n瀿+iL5M6H$䔴$zO;bCHwڪwU֭4*Sm U+ {}E*XS^#.qeӦ3~s+?2C9Ag**JAF#.oBxzQ#ț<*vA K?~Cy\Ube>\f={p1$a0{w@y9;bƪ['0ot<(k qΑH!Q:ǧ @H !5i WjKV\Ջm]7hlPPHІi#D~9ߥ}~v"R(H#%V Z#61&1 D(ZUil[%RQ$4(ʍ͡M5#JMQJHIQhDKFHS4TȥSFal%(I1X!#%:xg9S[4Uy~Ws5W$$g1$8)ڵW#o$VElO">#.4~Hgahf6ؽLKnR& ~zkpC%߁(2H\9q]=haѹS(BY$ߴsCJFqyڵNj{!:o?Wjߍ&XKX֭siBQݫC!bRbXK20'uE$fP#6>"ٛ).#.>+Qb0rcD>n.5(XRb"D.f+@6lxRXV,I.1{.Гь6&sDܷ/PQ. a#%!`СGLĈĽGI{5OEɟ'& u1p6eJ'LHQb`VAdb(#.LG`6 kgμ6r1JN2,=4,p| d<۫6jHElm#6#%("3@pBh_*UYU#6!պL@ @+-= #%]~7c#`>Ͼ3CftB$#!?"dꅄ|پ~h%{#. ,(`塤|ؔn2L3@>7Vƒiy°=ߵR|2Zf5Uo[52UQJmZJK*G;&u qm g*4-o^n2nF-tiD7C56&TS` &!*QA r]u+NAB/{,tOئӒrFAJ+)( ;Ns>]M˰#.L|R!)B9^jf<;#6A0xw;HA)4gfzqFcEk|!uMG&~#%Q;t@d"Ȉ*#.>/}Rb_cT#D:Ζʱr'5>|}._Tl=Բ]4X`C$0V9'Hv'FIj &g@>3#%pJr`{7s@q{A/U\zge)^=sDX1ƣԌ吰nd[wofXg)P6B\+E,3̈́JSuUtD19W53pTl#.ch` 0pP#.@n`%V TU*2/&Yr-ި5!P0E#QHa6P";^-@Y1n5͗#%1 ,!sYڌflzٹciD&Ho?/3нX2(ṵdݭzYg$&5D EPڥHA~3#% LC}Yf-"}qIQ%^FZTzHK"@Spu^<o.B3@DmjtalrX@zQfkQ A(n7۞W ":7`R7{BdXg&Sf% 0,DuS2cre38t#%a%筻#6LpOpߒX9 WDvkb+L#.ˆ#.BC7<D#6H2#% !!P X#6ɊH y-_F<an۵So1[VWe 2ptc,2i혎s4F!RAV,ᇉ$Eæm;cHo!@3k9Ç_'LYBi1@'*kM.ƣ66 AmL\RE΢Fe%oݓ*,u`ё91260o)i#.&Í@V$ltvckrF)KE M,vL.\{՗[>!mic#4!k}avߤljW'7q$[vLUD<jlH=fU̜L3a萒'>4OЌ!ZS5{XC&;8k\@r2n&='IZBŠF՗,\*V׳p`\HfJY#%㚨"ߑkk%O+vv&?gr{޺(UI XE#656ɛ!dhm^]04qs:0@E}hnLe}8܃^-1|2õ@pVכ;"'塄X%􌁖#%5`+IB27t#.NZũBAb+m:+BN\mgY[6LVlaDg@iDбP<B㌱4 l?nMM$Y'Btoi\+~8]-Y[tͤp?UQm2LqE#.q<<4x1 (3Hr<9kt Ơrz]Y1,̊sզ)F.z—y| +d׳F~Baud֠lŇ-Ņ3XSC=qth:j&#61iu]p3Bt_ m}i2H۠bvvx$3@̷gnՠ PJP Hq!Y%j`"#I#P/=fgMC;,LMM4Yp'*SxEYvMY@S̎#6ݹpf#%;tyA&wbOLO#.&?8L89ݓM7%89-u#. /L]\տ%.cxb4&!`,flLM#6p:5HdAH0̑ 4EqTk&x3)e(1Y`ǎ6! ].$#.  T0%DH!Z9ǴQPP4 wi8,H%Fl!6ae0\*hiD58h陖*P K#6@ALCD6DS*wB]4,6"wA3b")٘iU HMlE 1 #66v4#60gSP#.FHWU%0ME& 83Ą3mtHrĢBBꙕ 3[^6_#.c%%yyCkU@(צȚnf C5#6,"B@MbP l:uy\eXky+'J#.?>M܇)xm&-BkM7If sQfE뻺f$$#6DXfY#%W%UEMѐ^hI#%JSx|k0Jh(Gfuazf?n'ҫ*3#R'W((q#Hei3%< 쪌Jrz#67oSkJF9T@Z0TV(NAqKV+ma4}o{;٬66[[:>DH{hˇ}*s9eDʳ:d{T3D#.CvN]GL@Sޡ#%4C^WfDΗZR7p#6VX"k˻[$2%5;o7vȮ^vҹR#6A0@45*F 8iQ3 iGZK(4]-UD7v)<MFgV#34u*bQ GF읯!<Z H|pDA#%ϡ3jd.1<#P$xamVĮL+ؚ$p((7'&9w#6ncl!#.:&LYU !G(|#%mr,A-aNKMN8R0;n[5')L^b>%C38]6uLD`X B!:Oi悘n%ڒ"a\qK董LQ#.i1ǿ0 0~tm6V,V"$skҌʸ 0')HH2DO"ZͭiHJXDVBEGEE0cUƄe5@mn\0P`M`8#%#%Iѕ*Bah<2ƦZ\Ȥ H:bX1 8 #6 #%H*ߖ[|4O4o}#9thg#.`޻Ÿ3*\Y9)h#65P4aePM2Xl+%ʫEEDLNAGUym^VvV`%QV@INWP3:$#.EG^2S4#%ܥM2,s4PN0:=G,0!&*Ƿ=aօз;sHcif t1aS& 7!,H#%5@% EsRr_kW:*'õnH[h4׷`wAuu#.,q89 J;3O8~k1 $9%y6ZhIEG!+]fvo՘8`n^tY[Ba֛iA~_X>H&삉Xb#.45%!rKl}9='p>(^7/$jYiVIJ#6ba&Ċ*kLm&ߺZkYks<X员aޢs: #.#.+;ÀMI@\z/aٰn#%N=ǾꉑKFzFC?6(ukàXE{J9&rP|i4-J[hyôme;;#.MIqAq)\eqACD 8: 7$l}ufێ6c6ZAI%!pj[-[ +cTdLͶf܀#%PYl!VYCrhM #.ѭN0LoţJ1ȲnFJֆ bk#.?{5b]%OZZl}0 E!΀,"g4 )@w}* uAԠ$ALpIFwIϞ/tAO!## j U6t<4O+ta L;X#6ɨF#%oٔ1d@!T {WnWm'y͌6@g8AZ0?y<vulfPx"%70 'ws7u"𒎚ݗf500.=Ԗl:;X澐QP@?LSa#%G1#6 AffXC&3#lDhK #.Wa]\]ųɔSd5Qy7s]&Ѷs\+yv[yo[s!S$S`6m#6*%|}<L=AIYa<>#%<>,U!f,ʹ5j"-62)E4jtj7~#. D1SQ*)Jzh'̋x"#%Z"gBR1ȸ0ć0HrHNT$>#.D1)-fQlc+E*(@'](]4{qM_Osfaב$W #%'}eL|*j #6{.Ƀxv,!3Iy8Uo{,EE'Ft &nyBQbpt"@T֡dHE9Z B!JSjh("jKNkTRiI[RT l@njma0 NR(+\/*SRe/n.gUP dt=v9.k Ni`Mljh([;R*5XVO8"Ņk4#.y@^K?W-%lc#nzK!XЇovM)%I51R`F&#6Z7m+WZ(F AUdQ$9,i!`P*6+0rBF"EH(Ʌ)hlQ,#%h}qz/uJQ!"kWmF-ڔ5F6XXQZ[3:zYݽ%$kg#6с5#1KL5VFLкƗI$ȫmiGl/$3" *hc<jֆӃ90(8A K"2ڷnI-6N^56&\wY2nڊڂKdYywcMkRTȔ󺷚kΨ$ISa!H6bcMi^חjMIUZuujlm(eRםܷktYc)&A aNΓ#%#6qVm 3{䚂5Hd>QEF*d#%@ S㒡[Fhȓ(1P{5\&4)Dڛ7եG46Y3O `>p?~fE#6ݧ#RffKXZ#.Y9~07"^GM#.UJmG/lr$aB1 SJ@#o($QG)ļ.#.Lo.E`%1GP5/mlpB#%|$xk(#6(i 8>Jiߎ>9t(a-Qޖ3oUbٛ`|B h)Iiף|#.AqK5"gtdBKVw|jPOt~J'`O8)A.F'RK`,n#f4lQ!n`C#wƽ!ȱ|* DaMF՚WuzC<kGG Ղ]j?70EyZ$YϨЁǽuMm/ЅZ8@?j8睯fYF4d-DEC=vN|m:Pi\X"#.%t#̍ѨU׎1Qߪ@[8A ʍ;çKB4Db:[nX#.dRS(@r:|Xrc'pdlA47m~f1ޓBG_hGC"Ws;/~D#.fY{ao}ik*>D-(?Ld* zQf@ݨQD߳2Tm`=W#%w>uL䑙 l#%#%b#%?s/_H]>_|O?~#/?|/=GG__PSLB?i,ϳDy;"e\?E7;sL@Zm4 #%)40)*ǹI IFVh #%QߐjB MρESY8,g +,C҂x#.Ǒ^A:0f@#̏%*Ul·i\şyvǧ‡eԫj݆떅 &P"ƾrUr#.7_ua:Xs+u#6ٙ.7*jHxiiQ]d*iea˜T0eTh޶եpvmTYn44Bl`sO6e75m8q훓Mmƶ9TFdMmTIqx,Y.5myx*ŧ7FskCF #y{4h2faqS1a HF#ydH?F:o4)Ц^EKN\-NS4 '>:`wJ4#.p@ĞGF;tk5Sޒ 넞g w#6Hp,#6^>`L5RE "IEqOv].fB< 3PEATV*AB9wtc߷Y2 k\69U>!<N(K61$@ 0P2Hqw;\xH1M}lı38bKuɀ\ ,F#6"`T70]#. wbs $LjJlkY3[FmDŅS_EWZ|:־:?E3t#%]Ti#I(co۪zSl#%M}OFA~o;|mq>^F4?cS9ɬצU1"ǻ'_6+W] 扄8uKxte}C3zg,r.;}0זfT9P^]*zg_Ƴ8ΎQHԼ=#6@C&9Ғ#.QO#.D'G\U򻵊PCGݼម-v챯[kش|nmcm(oSEMAjZU݁y]#6.Q#. |P?!xndN2Ap'PQsp]f!OVݤ7Ub*J9H{cԼ(5@$fR'w=<܎_.EqPX#"(];#D)Qb(mLtjfh ! H 3ئ"~({fYr0mCN.!xN ,A<?  &^|YY?B-6t&E hN'c`[l@%AOeC.G ʽ1'waB+!,y>^#y;9ѵgs(?l-A!ԥG#%#%(#6Cۢ OI[BY=+4ks]B_F*N#.6pJqTE3oYwfӧm3,M/-80aw<}8fJ$ o]ax`H$Y@zn= i~1^#.Jz(LPc薪"|xg?I~"]B@&
+#BZh91AY&SY#*s#)w#)Pe((00b{@#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)#)}=yl=C(4ӷ޳>lb#i5;zhm2_<>;o9tzQRQ^{ZSvvnV|KzG)=;{<w`dkovns`#):s#)<t#+0fx{z{woi#)Z=2zR QJRwcPPTR)QU"-4S@UBZ]ojoxթ3#*TThm2*U;h;vڵϷ^݇<9=ؾvsm]M=cΎJ%FP!RAִ+nݎwZWMNHvz #+UDg%/l{s[m{J>#*{_}ޔH#)mkoE#)%ްrMoxyoͽ-n}½_uקʣ^piw>onۗtn%ju^^==׏wΥؒl4k#*m-n肵fmR^\kS7Ϲ;۞==k{Ruc_YGgu=D#)7ӗ;.|hX:E!#+vrT[뒝ݗ`v= Tו{خS=R-mGB5#)#)]#)[^szyn ^E;Ӳjt{|ΈO:"Ǜ^g)Wҕ0WrxiAk뵇>۞޳3n7;xtynǽbE`3EhYۣ)꧞/<7}}^ª\zR}ƕm#+Mw9s#+k/awv4J2]޼Ln T;PG{}{[#* Ym::#)J]zP(ٷ{[l\nu[PUUMwbHݵfF4.47zu"Xzҏ9s|C`OcB= ='Pu}}t:g5`wwn:xo;ΎZ5w#*4@#)#)@M#)&L#)O%44#)hFj=)B&41SS&Sz='4#)#)#)#)#)H!4C@#+i$$GF#*@#)#)h#)#)#)IꔑFMOj&mOSj#)@=@#)#)#)#)#)#)LM 2##)4ɢz&(#)#)4#)#*#)I  hhCLO%<OjhzOG2#)#)#*#)F]rO97jkى%0S=diyUhᶒ""1=g$=1c#+{ؠx):*e[\UzVXbŲ C^p:]4P*_v6:U4\\=:|\Qur<c -ޞbqX|D= &b'm*UXV5UjkXѵEY5WZ #)=Ra*( XYJ#)HVkj~#*m[j!d3P4F hK#)J)aDd FKSQ6#* h%IZ!#*FJDSA ѩcCLYSh%E K-4 bZCf`33ԑZ$iM(M4 ,`E)ji%k+[FK3!4D2ɶm4ԔlZkc-MeJZL1fdlB!QfR`4TH!`ؤلfJb1($D- #jDF"fhd1J0̂VX31"Rɬ,Q0" R4m) %&ED2hi1 (%+ Ɖ"Q3)iS0JdlDf0lK+EI)65ZK&DA&J d0Be$Y5RDKJl!E؀YR6dM؉")Fl2$YJaQ`,i"5+%6(R"I&aLcIBI%e5L6( Pi6FB1dȚ R%Ff҅&5H$$Rf0"ł%a1A$ʙm%3!)+(QPE"$$cc*iňHRf`Sa-IIM3 1E!DTآJCddLm!E1J2QI&$қFQH6cA22Ac0eLcH 2 )LR,!-f J`2Hi,j$MaH!ă#*IcQf(14Qja&e&(&Ȍ Fɔ*kF &hdbiE$̈jSl#+YRiHYSfF$(BD!BSkMJۢ 1Qح)M5&#*-!j6MFDai2F`%Iȃ134d1))j6d ŋ S-,ɐ&TlHSe,RS#+lfjdEledJe6URd[dh Me-!1!գ%AF&QZEHkD$mbj6hZLQLM14-4m15[$XDRֲC!2D#4FŒ+jUcҩe*f#+"5Y54lBS)aɬPPʍ$HE"ņi01$Ch4&1#+[FJ%E M4l4Y#*)H6DVYJQJl4 AE$&Fm6ccd)SL#+1#*1$d#!4LQJdRP5BT4 JBj-"ͨQ)$ hRb"m&Ƥ,"XB bM,TJYJ2V0H؆5"jњ"R#*1Rٔ@ Ldؒ2)6I R`0٤lfERZdmYL)aD!2!FlQIS1II hQZ3-#*$,FZ#+6,e$1RJm@F5D(LTPXMI5L1 ƩJX#*)Y#+($U2KD,EV(-&&e RC2Q%MDLQhdmSiEd[ZLRVSllCD%2 E cQQh$Hj)&`VdFٛMcj*ed244QHEQ(Zl6LE(#I$HZ6Z-J6["Z(HȦFdf4Ԉġ$hmd։R-DlldU4hc3J!,V%b*66*)JҌB4Y@ѢTi6ƱfTZJزmZ4kLh6mXLTLIDbDM2MmlZfZJke,jjmV&HJ(6I,EbI2dFF#!d2I4[3S܈*JaR,DB{$ ej'X;#+#? $I6|'=v9OޙMr=2'_YLvPCoE G.)$M>fU#G?~{Z?J/e+(4{Jvo\T55bӶ'$񖿋#*R(8'%f`I"lLhchɣwHLo\үRY9IuS Ś&U;1̡LU)I1H P[xenTޛ4V"S8͆@.9_Xk#)#*)X)h vIlF1IޢܼYzfձS ))Y*"i^@"nDDWXж (D%gF[1D!9$n䠐խF4iY#+qAW6d5snv$؟]mx,EAoOW[Lmː9x#Q~="!AeRhrꠊXOUԆ\2MM;Q7~z\n^+\Ѩ8wcr˙(ˮ$scK TSAr6bDa#+hgˍV,QJ>~-Ҷ.ofd4x_QI)饖PC`QV%.xۈs@G㡆[cɑ!$Ā|{kdu\(3)^vj1kgaN#Ux:8XMFP&<^wPCANME)ߣN95RDUH,ܛtj#+M4+1e)Ֆd|h*e,YI#+N*}y>劝U"֍UOpi)O$WrHoL[1[3&)}]uCAbĭ^JFmǶAp=8 fw^w0{(ʚ҅A@P#+m5͵>S6^41X?ó4E#T/65cOvn#)(5Q#)6i\-z˽zFiVJܱhܖqQ7׿O~FɷIoШR:e!x鵘OrTB)jKGrPaLֶjϽ$+?+ue dDvUдp$XSͲ]#ICHs֞M]J)")٥ZVS^X1a#+p(Mj!iL$0J *DbMZҗucg~,'~6QXXy"<4:QSxU<b_3|ᮁ 3VoMX犈rW`c su$߲)kGAF5ڔd(̍)/s1MLOom$|2̥@j"*i_mQ{{p}XaRiǿW-#*{iRJnCMυז1n#+61KvB9&#*Xe5y:UCsGڅ$侻ZqZ#+9JQo`XO q\NY'mg2||#ӌց\KP6@{"i:O8hQXPl-y)<!Ɏ1N8r~NZpdn$g esZ=M]}ʑs 07UXkӾvT9 0D*?<c4f\[Fy#+I#+CPXgCӛO~/gE0F:E&}ٞk^~NX#* `8FW_vSr (=Fj/~wfg[T<ytr ۴iK$'!rE s741Ţ6gl&?H\[ խW|-F&!z]\krvmcm~h0z뵍I",P>aSץ)F6 #*΃`wD T f(W\N0!oǺê3h4{:#*#+M-;2V[rf6q6>ݵXdȶ_fտ'qvNS#Ybd6CTEM'k7T^DAEtEܮwSe(AP3~- UKTWXR/ú%}E}?Bo`*~no=b~ڭUgLZ{vkܬ'5X黄n#9<$0 1,|ݮ>LT0$4ԺBQ\?л#m* Ҋ'; 0O'IXbW |.aðXOLTC] `GE"4df\}(EfF"daIήYd|wjfs|4Hm#*Anj0h-+mnnΛry=m`p;~i2 i>5e 2}֙;vdM&;D;/[0eg#</G5Sӳk~ {bYׅDQQe0x~r{*nf gsk#b󼺟S!P m쪦HVm w~W5FA?8.6e*DF/:hʳqhPmԉlaKR7gFTaes^o#*b,7K\ϫfF5L*9]Lu,a?ʝu9s}r~)|ݳ-7gց"+E3|E['yn[[(/c#M5;7uvkBoy q#:*!ߡ@2,S\&X@E$@jS %ѯ9Ar*[ڭ9مHT9#*|P+JemAŽisW9^!UdTā qpԡ)M_DO߇7•9;ҙ&)/)UEYL68:<pw+q.~?߯ƴwZQgYgoKR!e=cê޺ۛo`Γq+\Gkvji7XH0U4UtSfrD^8Ƿ˧oy=O*91btEWZ#*&5OׇNz\패gvǓ:TZ()'u\><:<&.#*iEEP~P9*18pM0޸tmH][џVuS)¡#*:I)D}U}g]_lS#+0D v>wNۭb8mDz-x8}^i~8ߕrn;wCփO'D׶ø1;x< փܛ*E''Ni<HD}*qlSvsx-k##lJ#+sR`4ř"{r6uTn<HvA˻6<8b?-tsPZk%|e ƅJj6J8Mf_~et"߈d/ F=Da3eyB>H2Hł~\nGKTCrhf,8`{3Ѹc0q֣QAdX"D+j_} Y؁ύ~?Omvij؜ٌNIi_qηowҡG8KU?d~:kRv 洕ՠlj90#)3mp-#+F,V! \bŸ G5:3yfXOLbE\:bEmM#LtбDk6<8!7WRz-sܺ%У$cf"6.u^EJ)*&h=خ9XKaiCBhJBllĨ黌?b<M`I0^Tj#*DGN66YXlGg FfJPF^>RQU)YSWۛr)quYyljbkxs# < Myw2Ԩt԰$#+IrHRUN|. @bX}#)A0#)An/\Fz(T 9h}F*yi>+_\z.z>Jj3PS}4&Qhsy[Q|*S&; 4 P!p\irj׋h=~9qm"1R;jQùk;FfP@OX#e~E`iF!1!8,]\=](aqNy͡Hi/U#)|7?^ʍ#*0qm|42eom>Uͦ.Kd}&ݪ%cB=<w?GZgPDdaGд#Nj=pH.]Ũs]!یD}7*gJ)Hg#U(+mV)Ul\*!",c8@^e\7Erv-4DܚH`6n#)QvOq1>ӺI7ϓ*a#>f0Նv^͐)X+4ͦxӺ*aOtl1Vc;ee`=xwc8əA nNPNKYn̎ZntvfbU'A#+Ks3%ʘ,"'Q({eڶӑKD@JE̱#+hYS-.6-s0(<1qۋȤ&X 5lS9;\+=hˑ~-l6Yn{j(RdW=Q1+׬60իHY.!\1YmaknèŮԸm&mHI'"7Ĕh2 Ď 0,ȅ͝,|Qu11GrߦZ]Sƴ#*y[Od@P%GWvT鎉?z(x&gAaBC?N\v#Ll܃B@BtR2~Wjսg@ƎTrJ&æs/KH}Dd#+Wg}QϪu:;b-#K/+.t`z}jv-J#|וF~zEڄC񴯬MUY} ˖p.p~Af<OAE;4JbNCF$t%_6! 4q=2 L:c&8SmEj9+r9jN@RfV9m\܉O'*?ێT19b7;q?B0+HLoDkݏ׻/!pEz#p$#*[wW.:=(qwQ>aUb}o#)#)r `M3#*]#\)lq񮪣-,"$.qQq|"I|^hnlۨ5y#*GOXځ!W;=::a:ATX^#*NBrAH 'Z.Kbᓥz@;@2Ù-` r/Ѥ_R4#)ռڿ>!fnB$xw H>u7##) 0o2p_#*DuH??=ίH=PYc ?sZkV3]d訉.(S#*סؔ5M#*:U@8~xf#)4_Liyp۫o2 <B@TUĎ΃dF\1cW6ږ_#*e)}c0t?ILd@\Fx˦.-.x9}:y0moڦӛ;Kv5G|]GSgg!5,`mm(}8U}=9 z#+qWjwu ;ZVic֣܈m?L|2lEIH41Ra-ȥA%Yń*sȄVFS{o==(6$,ы-'d{Vd0MT֓q{*H("'Cܛ3kyEk?ʕ} a.|m;w#*>2a&0֛:|Ǘ0dYqG0xԫ%[I(nT$~W!>hjI67#o.˖93O~6Fp+jf_4 ed(K.?2hfDj0[Ԡd>ToX[0v?}QU 6q>?kН#v%6g#{`,d>o,ôOrb5)צ[HmtDͷϖ6.w8wk3{e uUWK"pGxX $Tf~e$uod|c.9ߝ0zr2}y#}#)8Ot Q[]Y.*}`#)HYr-ފdp:_RzNa #+#+k9&)=#*cCWdƬ!TY^pxFv<GtIPEG`W˾q9ᴇ'LK:(Bd4Z L1-`,hT;ߛWkAr4#߬u3_Ib([uN: _5/Z#*a{cįQL69GׂtToN7}F"^=#*B#+ Y9ZX`P!N`XPLP"=n}6NFaתb(VÇBiw޺|SL( )#ֻFj4[W~baJ!F#fWAB-(0\tֱsTnS3 yfl+F2EI&f3sՔ'6@aENo+_WGTx^b'Gp b@1@00{a(zݢkˈtۡIb#*FWHqFYT_",P~յZI.3Ku-(T:EZX2}u!;vH Ua=j1‚s3#+Q}cJ7oQHR'E9jy?(=$yq#Qg(^ɧ;B2Z!(lNE fR:]y飙8<XH΢tnyPЙF:ݼ!#*lBI::˦849BÜV%UfeԱoߣ)vfG<\%s͍")X:?(ح8QC-nP7/.#*K/.t͕tn5<VY6x0Q↸)<?~B} 3S;ToA |*(t=5s'֢(#W24U>jWzm]Kv.wu`#G7) ,ѯ-qКUtc{^}v3N^+H$`1U1lj'X^,[f n8#)HrdC5ۖG.+#*J l-$ #aSM8^M^yȃ^ϴJZH(\Xb0`C[! N'r`?Sڡ- *#)ȴ#+dU<D\ u{:k4Im0PFΉΧEUL#eVI6NuJZv#*r_#cE .  ?FVX7@!#)F8Fr͵h^ EXfMb.hgN^޶5#*9f)dn)l'xXX)k0$`Q1aX@ѤK"4V-,KX!3e e0H'aK|7<6M%쮚1j߭4ȣ&(4d "XTMPÄtTd"+j1"Q$@C(mc3f-ݶ#+_[0* Byz%,5`|]mH!U#ڀW#)R፩`0@~m'c۳̟xO߶stDქ{^aeoѩ?7srtZ&R 5rz}~[to_({4qNռ)Jnt,X)bKoS/?R嵩Ñ2%@*qԏZ﬒֤g`T`=Vb$O<+v 8Y,݇~&#+?]D驗 7R0'vHDߺ(9ﭧ0^:^ɱ˿5~fbU`z;l&z+z#*s@ikxM,V AKs#n.݌a߱o0;pCǍ`yQZ(A+#)Zı`qS ._O^BvB";3)H|aξFV^TN޹-%#)LdC&xVDVANL@2,* 6Jd :u#`؍)cQG#*$CMB&+J,;"7b3^u{ZU,q;FǦaA!b%ن/w7@䡻i[ dikݼmME1w.#+W;K<8G'nXH߈g(q5L+ERi4xriȦa<jF?zc;GUdRˬ<> BⱣs|>~p6n)ɱǭȏ.m@nP$ܰgWGK#+EpRۖ`>#*LF#+):IO>\zR0CKIR7ɹ؏ۛT}6YČOJ9Uד,}:&;`̒6d],ߐ|?H-AuȹHƇ4ڂ:ts lV+TF:}Wv}t<΍֙y#)*\r7#vm#)~tsV¶4&`l5]ULI(Vj1zxk.i&0Tn&9ioXqxQ3J`r'~Sa0mpJ%l//Lg>#) *#M45~(LĴ룫si·& A7cΥ7՜N.-R66 5k5ph QV#*RK܉jQc*CPDo[Hf#*҆K_vjN f֡55iUeNQ8'@PwAo[N=媼[Jѐkl|{>P@KJ5th,+^ݧGGZ 6!ZS<Y%o_.8#*^"?ju+Cq_S ~a?*VI|g^Hܧq\f#*ep}2<cùCbG7A,yw@]_.ߟzuj#)Q!^wHQۺ.O^n-g_TGӳi~͢7`6#*L=VAE;ܒG<}'DD'u CRzi #+#T 0d bZ0 |Oіv4[#*?dqXW7R)_⭄Falg2#)Gh#)F2O??#)RPJ@J_mϦ8q{Ǽí;7$HfG3**9SQlFzZmId& 3eK8MlUjvݨX5RhTI\Ki_뷇ݳuM#*8dmv ,PXTR* @I)R Wt/bkSWr46l_q<Bd|VnV57RɢPљ.)92eAU#*Ҏ (aTUS!<a?~;hnm6#E,/8h+H(#*mm~ a:EɃ*W77,u/R4!DZ[0Vj^?s8kVlV#*),GG%@?֌QDY|4pǧ9XdR( YTsg CV"KQ*cT+ @C`z"MD a,~rd[xO#(lYCq!7iCD~h&ʊR@pVJd͹e/o7gkw~|=kL=0-ۧjCdigOC]^%05#W nlm9V+G@R-`":D& ^]J|lX8萉(3y/Bƻ] ]̻N,f#*mTߜ#*-] 8;od7W|Fp*9jʞ6ے@T_0cKޡQiK1*Mo#*-@;yĐB"ьӍ˯x_)i>'Q~;r}]5H@U|8Lf-Ȁ7J#?v0a ^o(hV(Z0yo6Trl9Du#)j#+NXS#*n#+7:MvWȭ-5#+Nt|`2JҤD6梪+0mD(pRsS_ܢB~*Cy+eyPd+ʂJ0oߚbk'oޑM#*/S5 n䟦?!_ Iz}$a>TWV4(H"4BUW%gr4#+bXX H࿎A9įXTCAצkODZW(qHO#*P- nH=9=<9kFF7 * %T-#*DK):TżWןiiծ s?<)]ϫ槳.>늪oubgVY\8yl鵂v|J[]r{_n=w~o.[za"Z×cTyOz9{Go M3hr+Jo"_}ޭ>\=>pm6\\,'vz5>S[h=oВ=^G!#)ˊ@i>+ۡ]줧Y!=7~w*lqdyPw:Nm8}.d^C٬uƌ45q91Tϡfk4kmiގt= Vh]L"h[o[ȥ(}Jyt;u4kc#+`|LvRT-?9Q ;˛8rnAۨcyȗ׾V{=[44<T0",C5-:=vGU\RD8<$DT,4 w{W=:}t4^OBG|.͇,qp;E5÷lmj*<K j[6w#=+c'!;^u?}cdኪjߍK{{HTͶ(Ga2|v'FK|5gA՘7jy:-Mqp?+W};hmLa82l>ͼ4Bu"q+QهWu0٦KttM׌A_,B8aSG`x8WUVϾϷ<?I]F pc?gʏN~G􅕮wN^fɥ#*F~%P=1B,Xן ayyiЩޢW39w_/+<~_ AE(XbïC?s:~XO9S07w>W/gf}_۱OSߠߧݹsD[lٲٷGԅ۩^?0{sßPnML.,gպAKSܓybhvnO\4/߷5FRp7 Wit|?oGр#L8|Ǐl?-O8~ϼ)bykG*By-cڂh)u&fXC6ԥApoNnvu].}-[O'I'm<EX#DPw/)*t<f/؆Α â@m,#)(yPƪ0alKC2u8ekhltx׺b+r4.l?"r:J알렦i^xḭ|wFiG\8CnRDv0`5>r?-(^g`E38KACn>i螟'o~sMX</#*"+]iDWsdяְV)OQ//ֲQ9\nZ沛 c"1ƺ>ϥ]p?!O]PB:sG}eaPuѭ3e*qc/`'{Vi h.-ޜTh8h^"M+x7u&CPGX ?= U<ͮpB/~Y6>f4W7Ø yA?.?ϊaǤQ=<!JU5K§XIғ9SAQ9: 1Eztϣ_]U÷WҤrw#* DLXofq|\?~(#&q0/]:lm<Njruguܯ`=vvNB'i=ш{c8~NpPTh xDoa$a*?dSC<QJyGX7Q#).ym6`+w?Al:9O*?pf@9% Y`#ŸF54iTlTԢn؀7#+҃,tM`Vj5fYЈD0QJ[M`cXLVDRE,h5b9O#*Ss6]&6) [x90$ОV6bxȏٜ3((>~kS< =a5$3Q]EcXwBRdK`zq |& "4i[F2DH@bi7C$MR%dl!#*YRMم$slmg:> oϗ噧?ƚ?χ>}8$2piY2suϖ}G͕^6yǩVϿzxF_.2N'},Z$A1lx[~{4,eVMqeo950J0}(HAáߘz;=߫_WNgyDA8s8#)/QM]qt#+ed,ΰ8t:ןYGa75_0Ku;z5+uj~1l@L/  J(} 8=uQN^v&1[-DP9+#+0q,j[KAi$JD||ף^^|R"Plch1J1BdQZa++)YlW֣F*\mFAKTQ[*86FH2#*ө7#+F5ߐMAS(0F&bpuں)lcQ#*c̉fU4::RpI C␂mh ͧ`2zXUֻ]b0Q$I8 ~UIG E,IW2*EsEQUڙQdec=Drf |AI$JCo!A"^1b޾޳a/NNdjEb:aN5TR:s^>)쇻v S"?]u>ϖU0H#ǰ=z6)R#kީBݚV=A{_y}~m""t^3za6Uq8i?oQdOepKAr2ÉN 50ȏh6tMHneD ~zԪI%UUQ8$whM 9,HbzyB>Sˋ!z=#+-c>Mɬ[ 5<Sd$>]L5o )ٲgF1#*/XnFDy:|V519$,0*IHKק#*740z`۟!OzkF Tf@"&ˉw\{[u_ץ]`Oyyte(7bdk{e>\4U!hy œQ#+#+XRfDQrKOlfr41-6nH)1=jo\AyY=Zﺊ#+Fy# =fhPi12X_;&-/؈&D+剕.!&횙7I%6mlst+#*߼h5`yTeLzDm,ݹ#+$d]#)˒tjEfޭ;Bm6,q3cRblfrP&%L"#)n嵍G[#Hcm'[C`2麰!05igN=Pp$Xj0C^e]w :wP( +8F )J`٭ҴOT1˷rCwG; MIg瘸(!1&^3M8a=A/q~3A}qBݎtzmG6A>#+W-~:Sh v-#*..TAbX U441nEtH7^ZQ"TOKVItDqH2$HMC߮EĉT~Dgl=zM7Bv<Wk@KEȴA#+q64(XĸҪUN3ub!П:M(oV~[~gt\`<}^vZI<=U ПT#*? 4.ZV#qn&:xgN|4R뫏ːcmcҴ@}u{#+2XHQE\b0AF:7)z6EZy6fD٪\]ţ`QeAY5dC`1TAXo$e*QM5CEZ-#*H®YÌccCB;:y#+iypMDˬdn`h#+$.cI6] ODD{5#*8±w(e&Ev`j0tE&Y#FS%|FX3g~vnt.1z~8[3ʦ6ͪ[ mϩӑ'̎p8QZmh*`Ωܔ;PVM!_[mmcFSf=R@mc1_C5-ƫ:ٶ?8<oj6ܑRn1v6DE $MBws=m(#*fzmE=a43B#JHXX () -U{j5BŚ<Ci7t >c5P 0ڱIqkYp80$',5ùC:˱`X~qXayi{q}CF#+~a]]7HImS/Lyݣ#Zy t^@>QcV+UZL]IK*Nj;κc`oȶ@A@G!xLRx-n:>%!Dvx(6)vӽ#+6ڎ#+ɼIJV;F-o#+OX#*.:1jhD7M]g2r3eI(ijb<@.6bsœ6Ea`0cZ67'8+].aZu `NM I42 ils}ncV<fm !(n,~>)2p:䤡K9S!mD;Ӗ#*֎)=0b#%To,ڶ54"N'"1$@Xd )ci)xAQ5{؉TygWKgQY˕$׽F蛎JYY1 D1ʛtik IœTi#+uoB]O#+3"0EP)<TG$&I7~WEI#*|qhPp%ц=;fw;}1XW;?(lMgW,_1V--`O0mB&坹blqD>yIeF;<A?h& Jzv#*-d l#267'}_YtNWd;=/fRͲcR\'7И{15HtΚ؞~ǥ_iM[AJ6E;#*1(zZUެM12. DNA2D\69/Z%뗕kMv8ux#+-2tl_.#*A$Xfߡj7<YG| NQJ}Jڊa"o g'l/Qwvr{#bO@w/&!EU<̆I@k39Hڠbz2Ǒ8zGV{D#)z#"#Ƞ Ei\v.:zW&x|n@\41i%+kx蘞AC5ƕQʃ0FB뙌012ϟm޾",#)̅L 0~I}ڗeC#v!`X#)$;#6CU*?#*vg~ GT$'C #)7QnVC@923^ȈpIcg{7yIvc5#)s.1x"]m GG$\NjI6Ӊ)J{F`??[\mÑD yŁ)x0Lcuȥ%[YpF\#+Q*ی[&-hKrIpCG}OϦIb%.`&1CI5W[摟#+ݮIHB4,tJzqU]nkt0X=TV*y:vB_aG39#+"q$@LT&!u<aoWziUKOIC{ !{MM"92I#+Xv\sG't&A钒yYGfXFީ:f 94g|)ښ'm^8OzLwLYȄV}UrlDk$4JhTψeik;#Iq1#6O:U$UrƊ8/vd7'hSK'pk-at($x2 1~HO#+?p=[uL~;ii1#aAIXI1A*ã l9HZahJDG9|8#*Ӟj"!4~Uc3h#2к5(rd#z4"TĽ~;DxZjm 9QƈG2'tBw#+ϥb_;2I#᎜i܌"B樻㼄t&p71$RLf2TOsũw)nL9)_Gˏq\Cۺlp] EE߹՚ORsK`5Tj^QDT}1 D7ㇶh0*:֠h#)Zi4QRZR:$:h2`U6eV1#*vYƩ"er!\30*+YQ_Fu-Ŕ,dDQhtsc%[R f{3Ak٨ӕ %dP񎶖颕@)է)OHg}b^j~8y$*GV7Y{mL|6w(V$s\\sK[Ʃv!sYPN+3ǥ|msΠ&:/OK3m)ӥ鵒|tp&`ȃ4995l2 ۙ{#*-\d6V^!6xUrXmD6s*kb'&x\֮]Bfێ)aMQ|ѱݟxvbTci|#*Y<hYo F I#*K&5ȣc:=ֻKtϜ7jmcЮѠ)*Dw(:@*as6Py#+$E)}'}'nkcg6|$ PF詠or8'je;MFlrF= *\:Eٴ8بzi=Fo%*ag37s{>g:$qCnsl \Œ=<U"x/O`,'{Y E]*4XAyn ǥ؇{*6z)~!/;cjQrlJ{&y%ݍ؋u,vut=Zj}o8ލ}s^G ap%N4e iQIe]RQحfD߬FRkd{⛽Wv߯<o Dq¥cfFVۛ[hr$ޔ:W?QVܘ2(uOLnߢ|ɇȶ9E|H1+Nj",evYsBSF:eӇɚH6]|icƵL#+ e>#)vy=<N\봈I_*Z"&e.Pj⚛?ߟrbgIXȢ+S\}uF/>͋u]d:FNxbT{IZkΟ$r[J(7ZR:چ齄vlh[0^1De-FsTfW#+/期LYf#x#!#)t֓nW4Ubsa1RXߓL).XmzQjJQgXo=ss*vn*pW[ڬ:]~p6 Lqi1޳={j_h,`RWs}*F[,Úڌ2SXBX>Ǽ9Fmd"R-Kk#*6:#+vL-,Ztw&A%^]7JrpuG8/qX4Vتچ-. ًERt}yuQ[Cos-&RS*:SʤR9q%y㯳L.ܧ"!̦9B ղ(YI|m\t3slLޡ -k^pmViT#+3XW (]Jl_'AZY/#*qƳ 2( 1Qٚ8Y]{>Dob|,KӃk"3QCz΍-%K>qnAZW\e'(ʺd2^$'ۻ*_F.M佅LBK#+:xQ5.#+y*f_zW@]뙆р33J<K\Mm%֪uqx{6^qtoB%os4Z~t nja6;RȪ!m2h#+GM q rsvyhgImw="]Z H&ѥ)W<0r9I[dn}mKTYyQr:5vyWKC; BV$p(6XJoLPP#+.w9{}*ݧϷ<#+<e<lN#+nBϲ^[s:TlvMʗڮ΋H!a-9<FeiE`#*[vF#*maqX'd+1w ۮySeUG;;I㉟/E7O~Ϫ\Ҭf#*-P Rl(TEE&XǎUW_:#[ ss}vZzm9t͹en"7EoTv8#+g8 {fj*b6hD6&݄,Rjtq0'H dJ({Ҵu>H)#IY_#+Jp|JBNaỞ`Vjkٲ!y\piR-A b g=3KiWƕ}ZKQdvivW#+1Yjȗ|C_(يG`HU\,:^8Bq-]#*S~43 Sjn{w46]_ b9hx #*ӓcZ#*jZt+d$Ō'[_zZC1M+g#*#E-_+g .; @\#+59Bp!M6iPg,21T Y)z>/#+,:ν1KSo@@28 )Vy-U~vC#+%PoYZQƜ6ϩÎ9QT~uj/ EJFjmxR$ d5Uݴ_#+q#<<̈GX-;Z/D5˷WiT 2 bhCk3d9t{m!ˏ|xʅÏ:/f//xN;Tǧ?]uG<3+#+^w)ZsÉDmyL?港LK|RԎ#*]lZKMeϔl0"DYH]n)OI: |Zz]~77Mts%YI QNuTLF`oѵK KM)U;,= xmDt8upq qc%M)l*jt0:>1[OS;dē{79ȷ߻ӻ;+o,a0CuqoL_T:6mrEK_s){g6,e>t&<'k+Ŝ./ՂTې!#y FG{S6:e#)}+ˀ|1ҷfN<1ӓi\7lQloa#+d$c׍A |zպtǤjR<G{}~>LR,9c C;-r)ܩrĠ:9UNmƧ}[6j5tM x/p_ftJ6LY5T޾pGC{ۈrDK#)C,#+h:- 0!C$trYiNWaS-tnZLCa.U*BVulJ;FX#* -8St,sA<EY{As!LKl{nŶN.X.BNU:i`BNTj#k-RE9Qr>ȋ3<{s3r;Xz̽Df)9Ku:mhEdׯÌl}gN)<V56\6^x0}]`2+gs@x8lkްŭpAa&=}j,x=xŭ<K|+9/+,| YRgl%k^R0~3{,R9>]>y#+tTl0-AnC͋>D ϟ{uv&ɜ_UHȔ##.斎ZL_~1/ekel}XqaGW8$i,NHs<#+y#+ت^@^..NXѩ4釅e#)w.mMB6֢sҽKRBa֚_Ru0r 8z\A DWNxҺiU~iAxb@ is=G!QDbJ)і"#FBW23fYRDHҮw{#l/e3-v"U4EPMwg Nu.b-ڿI!"`}f|~ݼ5H^wlDm^Bnb3F۟޳j!l /VԤk$%Usׂ|&i{)OJ l+}X}6z^Vsk1I0X>7pb` -e֍, C`[ȩ{ΎRz~:\g5~:e\nݮPU-ѸFqkZ9d*tۿ5􃒞[r|o)#XW>z^x݈eQʃ#*`1NA;o$x<Cfc;ԗ#Z_fg~A-wD s6כ:L]9˯P_qNK>O_3E>Hا\ M_~:'ϓ\?M*=Z WxKcUb-[ 3`#kcdۣHdIZVmA9W5~!ی[gyov"Zn#+sԤoaӃGzC۝ &(B~[P:mPG~Q@E#Ta#G]8s>_Xxhsrxg24jf3 (x!{Ux+Y8;>w^`q6ɇs%,tF._\T Rz |'+mloE}dxCb?ʔf܁Wքc2-Ix!bGe(Ô"LI'Kbjd¤d#+oR`b&d#*}g:Cq!ezFy7ѹYTJ@ ;X]M;otQ_UgXA4!;Mbĺ#OC<_ˇjtnPA4d) '+U<ʝ#*$r7gˍ^k^˴pejrBǿwj m$?;0nf1U##+ڔ?򣢦5p&Z~r"Η#*Iq~9;WmڛTƗB!Wos}1+/82zRB+MtgmAH Y4rЖh|K::q,cZ &C(;&r/@tB,_@יg?2/NW>2Ġ#)PO֊9Mw#(JL+G;Q@{<K ! A(AF#Wyl.%8vλĖ;e6Hړluէ@bq2"Z"@46o+,q[~-,k_|FxI$"ARI&ׇz:r:L-&Oxj--<ّpj9I2KW}& h'7#+&-{$C#)G㘋`$-Q]ʿ8,dU,FrޛeE'Po(͡md^H=D-ރ=b/whYbz96]0nCq.|#*znbb5idԐҔػZC)r%a{R9#MLЩ; $=Bj}HC嬉?Gة*!#*KwkS?ܘ#+}s"kU@UVeU7oԿv%\īB#*r\Edн0_菳|\e'gs*j2l2yc% 3psy5IF{ kܝXrȦ~w@8>{)LW@%B!kRw!%"_VU<'lC&Vnp#+d)4`/\д,ԓD xp ƈ #+Tv%g#)L#OFińCϋlNJa`8&`Fvk}N =k!z13H7*NT] ]v̯>[0nݮiGTqtEʮoj#)P ̰L{Ww-%fᭅUu%:F`oL_]P#qlQ#)}"b ^<#{o9*]U׻_셑md(߄ 9_7J^UAުBzJP+j_#*F$zӇ/[O2FPU3) K^bO|41 5qXG]u/q<2=c-QuH@i6ā"m!;ϕz6~}u<VF&:B룻##*Zk۲٢\DAN=W<1J`QfYA7M*"%l+a僿 (1A&Ç]3hrr^{䨡¡lΏgV#+@D* f#!Of[՞oen+݉FS? q@s#HHj )2qށ|7vo$L625g$&ESPI4JΎ`ӌ }#)zz qĆd-./ԈyuIQ Xn,-p#)DlL#0q'#+΃ΰ_N!^\:Bn/ c<`IßQCmQyE㷤@iCT%.T1rLyeTDgF|Fޓ7ȃ;#oHՌʼn?MϹ\E#*40B)hB-L зUrI(HZa<!94. #1'֩#*$nE"#+ xw .Oۜū֑BzbF;f-]z|BshC%B[q J)羗\l[st Gˁ +Q`-$9$(!=e,ˢ#s\{uܣUA*O.6yRN-_x-wړ6JsLC|*D pG#*AΌMnioBzz2@ͣ5#oE ܮPءaa!# #*0u.-?Eo3 IQq-,fG,ؖEKn%vouGˬFsH3s|km~$䙨 BTZ/y(й.DSctayﲡnS߆,7"9axhU+0k^2/$j +peU?~7Γ.\0`P#+q7n%(BRqT^=G^9w\so{I#*;[^dym_p2S0JN:B+o|`^4mC{qtZ@zp1$@o"0CY$SimI(g{w,R#+ZH#s[9Ҕ98'5 Zʀ;*9:  Tptlc*S#*yv{mwT~w_(BgzVFrWyaǐX#+ a#+QĂ??8lu8'.E8pI -,ԨC)yOW+dFǭ>Mj~5'JW>-8JV`s-tHm]pkefõFctmFhMr k^`GQ^#m#*#+t3:PJ70򍸧KOC/ooOߚۺܳl3$m]9#+T@5ME#+*C\t(sݰ=% b(laM^~\GNrjμߑnSQO Ijcasብ#8o;FG[1)O!\Hμ\o$*В?PbJk<G+Dqd1h ť+v߰*݅(R{òx#)i>]q~L+|̓wYgAUOv 0OH19q<r{C'Sߟ^X $#*- CȋP#φ?[S=iᡀ3~t@/z_?^b*Kp#+Rєʢ2Bx/Q)Yƻ81 >F:9V^"p,_©*",{|st N")!{!Q*E6F:jaY1k{ۉDZyJGJ ydo~#)K|]`hGgYE>AR?xpBN#l#M_VDb|ץwۨݙC U.Vfr;+_/LOPOyQ(#)zXlD'?}sgoo?7}?ӈ/Yy>Ww~`#)ofXU?SXq|گC"Q .Y0~t fSQ#)rP\ D :D,be:97ء4#)HҀ2a #)+#)Lk),dEI:\tQPY#2\5R&MK`K%LlJYFD%P*m:/U/XK8,=u* bwy7l8#+p\Bsx$AdCa5!^dˢgeн1Sfbxިh>o-cGhBWz}w\9gEN&ܨ>Խ/2GGom_f=3r@z H6 iR(V npm"&wa!Hqs~!/1xۈ댫{KqF|!Y'YG&͊e'uZm~Dw`wFjVP?Ωi$gWvll<㢲YDtZ e1!!އ=ΏB֚c#*q{iڬ?8k?`6bW?"?9ٝ-%v+FM1fY)TZfJbEor,r4UU1UB}j#*O{r;# \C9pr.Sp70GY|HDHg$" E=_M-6i#*wT7fRn2AðDmqL(t8d+owZKBl S;4<'vdf-04 (x#+mVHY֩Fv5ѿYFE#*:8 Tct\So +! :,jθ04q"z^2؆p 5VD9?h4UtXiPM`9'rx#dN(PeDT#+H5V|eY*ث\\%Z櫥z/:׭uʫ)4`HIdMWUt/>;@ApCYfj49<xvt/#*-ro=o"tkP/c>XgOɴVE_N9#*cV#+rë Y_1rUȥXɣ@Ǫ)e&I$eT‚$dSZOx#6>7+u9l(փB*(튄ݫ{mklYHJX%PIR$;<6t:6[@<X$&I;6!6!Ւ<2 2gu: ;9ȡ(AVn N]S}|x׭?7$9Ř%yRZP!Pm'=w=Gpn#)MI glvoz#)h%RӍ7P5"#+@Y%(9ҎgL%J+"##*tn;2`,!UHw ]!!#)Q'rQĵHڜv8(SUXԂB, P+ו׃<qf$.+|# TU<ɚ#*A#*{A4ً5H4n=BBy>%|IB@D5dXc<uE@:oudlx"gwӊyNჟ.dzY:,%3g`}〭#+CeoP;c.L!o@?l+ 2R`]Qw4lQLHR%x~]FKBmT̩7ﲓd=f4{C>gJuKwJ9*L6T^ˎ˃^&a Dn4(#+;!v@Ay&6cי߻gӖMr#r8꒩J-JD("`kJWuqdyx,Ax0qǎ4 ټT94)H齁e<ꝑ`yFEWtww댪wTKFN8#+KFd&d1О'҃==[ 7ߜ/7o}xPp|;mU0/Nz$΄ج\4Qv.kyOCh*r2(%*Nۥv]p9 :WqG;aL(W:궂}k]]-bz}>kҸim{f|{=V #*'&#)NlbJb{KL UA`wLjjm 1yw&}֪@TLv9\I^:F}r_MC $&N1҅@"1#*@u&{kuvPć"ŗ N5Q\\DA7')Cq#)r#+DhRHz4y.(Pw?kVsok0 P!3@$QM ZlƖKQ.1 X~kBֹs2WySV#`T:l#+wH#+uh~^A~s]Ё>c:R-¡ MSvƛ-@@nXʌ>ÜL6ԎhQr(I.țKf$nָ6W &'8@!}J#*!B CLIlLMm14G6v8F(fZ烉EP{% r`0 ۟]j15=So]i?'P#)P׍K-<*x,Ԕl}Cu7aX:0!,xDC@m쾷FÓ$c y;oLIW8 J_δHM#*~X*ve\ٔ<zd4yo>pβNu#)KX?xϧ@n;0>#+'AJ/q.Hp_b}[<,keqK-n^@ ׌o#+s&)G<#)bidz'\Ayp>c8v9&ycımf>: v+#)ę<5f  e !.XZgz<߆OՖ7:%;xߣ!)'_·\|y90#*Q̈jn1Kr[z?.wd'o|rGQX:mf|"HZ-jE_Q hH#+#)H( eٞ_*43#)GDw#)G#)"kO^#)C,?=3Axt|'Q@|}"ϢD*??o9φ`pFC ^G3Za&nyEukʽ #9r ԰}%'63nMQםO7B3f{spRJ|}@y]nGEZbᑯl-͢@wv=1Kx"#)ˤ~P!&-AQ Q:^G#)@ܿ7jLjJm[Rlve(Wnp'y"H߃l ]o2L΍Y~ŝC375_[Pb l?,&Gʼ ؽum{~aOe'CJ2>3Ҳ3* {G޽˭>#*#);9 h`6P#+!#)(|6OO#:BoLN:?mwg՝_Q_Cq3"n6 Ϯ;mP6CCKyT"œVtƪ[ÍE NZV@L~q98:Nαw6oglyϤ_~N4lmC<{=\ݜѴy-z,#)#+{2UnKX} jijYfs(/6Gݤ4nkyihPa*\ŷs R#)uO9)W#+]Eg"aOsGox|D#*3KݭdU=#*%ut<GYL=~<n:wӤz#*#+ */HP(HCsE{򧮼RFW^DąH0y1AvW!+j]C+cg(+P"@>#):u~w=L#*_*ߘya`{;yGJ~Qק-#dy9K"1s8l#*H⺥zcAupZQfd>QPhp;S}yf.Y(b4Esff#*!꯱6(,yb'?j4p|]Lq~:pOL#)̃=۟:EyP9W[wA,0d(Sy 3Ff LNϣ;y4[q 7yqXj ]Sv4Hu3^QLB"$r.kߦ}v֪M­džV3BADR!)FSmaz85Ղ"4lFlx\C#c`YJ=]A*w'z>1Om&k*#*x-uvNwe2N@@+[(M]dA7FIΕAp)#)TDENFr*s`#*e:DHyj"&]#u5;]%'yfoVo5~mXgb@CZ 6x ?Pv_dѝwjsTT;s.lxt]'meڽatp.C+i@ ,770u#)%JLRW<zhW I!mύsܘ5(Ǥ`g=Rà5kֱxkQQ5#+з/+&#*P̚H՘/<&j-{cGlUsqvsC`p,*)G<nh9YXխW`5(xwlUyS'ɞ&^ZF+z6脖^|>褑w.?OgEvk9~$̢CH!ѥhbWgzlXuA_R"e2"链)_Fa.uDumlCoEcΈ[ØN1YB`a <?,u$'F>MK"t0a#+5#+&LKܠMbUy`[hX£2S>YF9\3~裰X##)la׼ ᲫcoxʯPr3u#* r,HeXo9U^zISeNInIB&V # pqyCzY]rѤaņ7M~XEeDBI TܳqΗuɤK9D[6egE+KUy 07QYј AyEg'/HG&'w=}Sqou;uT;Ac4Sye0vԇ"#={_xֿnX" %.m_w)UWG뽏*%o>gh9z7)s>h AKՉ7Snz)DT^Gm(y#*mTZruH[l=/vmo(PaN#7_h➜iS$ӨRwѾw/k[3읈C(Sa;`Ifuyu)'GU*<x;I&fMI?S2E>rΡmmXqrߍt[)(@Iw 6ywN;L;\Ҕ Xz=nү^l9$d-wrލ]j[(beW8s_cA*$=.=-z"c:"X=W+~d0zOɢ;)p;}ֹv=aɤi.#+T#+!ؽLJ%Hʔ;pwJ88#*Jhы=~e̒㶉ۏg-1Y|佧A<yV!{}YQx}{ڪۗiY''~ms&h\jdWMأ<i>/K:`g0h7tq~{]\ulE*mk2#*v#*;jx9s6xR \G }bq|j't>d='촘7+pǟ}ޠv2ʼn!̝lJ?AZ9H^Me?=`yګE` ځ,E^"G>ښ@p KcIgy`PsR( a_sCrZe{pA8CòtQ5j~2'uKi7pRSᚱ>5G S>kDmo IN#+r@({ 8x0P\BQ[čX#+BiLxrD)fKAMs_p!)uӼ/[;h/KyNi6+[G$oܵQbPp.2|b龞h "&jt&J\E/z aݕ.Q!"_[$=؍K$ÃΉ <;24II][9ϻotүN*0VEG\.OgĻ~#+Vf:'4e(b<f=Pe<\x(`BB*D0au"oo(e(%c:FЮa`pqpw;pĸ"@ "J9$IHQ6T*ꘊ,0 2B'}Q<P.TxYρ:)Jrᄣ) ,, 낱^s¸ү973-(KA#I YoluQ\|םDZ.z>D#) 1=?~qfp辬jtKd{ƙ:W~C #=Y?E"r,Bj~d;34V?2?P+YTI#)V\-ǧ=^19a+7>0ׄG3f&8Ly}2WhB?}٧xh=GΚzCmA.zDg7v+;I ˨^#0Wj<bO8-3J~)¤H(ʃAvD\{5^_j(ە!rLFI$/`#)bSϾ\䂀4#+okaFf#*/?(6$=Y*$?in) `9ʌH6W}F~@~P`H#*M= D>mR1+7I檻5R@#*)h!nqN5']$"KQkgۧ?=EDsmr)DVcL4{xu]h,` c.[ׯ #)U'57 gFe#)~z)Aa`Aָ_!}$zf-nfAaAɊ A/pF `c̒&k)Eџ+!]?e9#)X޳Ě4P)M1@D9]v^#4JXG2t+{Ke(ֽ#+0Q,+*Jb$X Y-$R%˻(4g7nmW]fa8quW S>D1ʢ+!:ѯ*9jW$3Vf)*lWؙ(¶y!vQBTTr"h4zI͂A64JPoZ8}36#)3TJ#lU#*>I{^n; {$Pkma!|"Q|AYJ̐Bi:#]4 QG8,^@28pQVs;HFm?Ou`W|r@#)HWx8%%$cU^oz?k1su#n:g] 5|Og-VSW3M+곍"4k]]!c+|]3SX{^%a0fx8y =1\`zN}ˡ9)h޲-#))F"It@*JL:Tq[ǽ9ybSNDK*1A`a}܂#VD#*Iei8Ze \^! nEm׌MoP<&GjNJ&dqY-8kuZ7s$ qL4Av#yH%XjBu3tgL iqTGQu0D_$k:zgj{9i+ ADZ#*)MG3}ޣǾwUsvKyM>Ku!ˁ:q76ތ&I6uT3p$e)V#+lLk3REUДZD #*}P~}^psM㝗q=rI.{? {߷>Y0BU!"cZnP.f[%")apͥtQC:G3fm.Yr&K}DJ)6umv0 Hp#*u-}~B yVU̬#)E{ p ]|-*uɵ*,}rVY>Y\l1|~Xc}E:E#*;8ؼ{ZCkNirrT8=K#+Qit"}Ʀ{͎7 ^oa۩"籝j$rRfkݢ)' } #+tǩ#*WLK劫[ϫS;uݠyaqZ%qulv< d-#*6Sȱj[nD|^N2Psg&<W./c.M;>/dMy?RUT A)S̗$tOϪRJ0` ^#)moIj(‰߫C}f#*NϲeQb< #)pIٶ@kz7s8muq/H2>t~6~ŧB*E@9[>RLQ (dnLلF@dd0JD pIe!$ 82#* fiY;m!FL=xGWc'dzuQ!j߆a6ÚQI `;{UR&nFYKpxTR⒵B~X:^zmVPG<u`06 `vqpl#*&s\]SxhմxQ; f`A]@sys %(tceFzX#*kqcg~zw4# sqg 3'Zh۫i9 lMu3ڠU)#+NR 4̡̅AClݽ\"S#M<klax^Ʉd:0`-L2Y\%kcc"ɪ"5e8:"55wȍ'CaunNȒIؑz?o0xOr|IRB®Ȇ%rDhr c+ k(HAAʃqN<Oy^Π/y߿#+Sx##*jeA *{mr:a M̀b&ifWBVdZ>R#2P.=BriW#)l$; . T,{?6PHӂ Q 5A~Qw|'x::J1Ӄ>k]NfOc+lr]h{'Տ}GnŰ%UGDB-CQuPs_Wu[PsJd&.B$M#*#*b܂Hlyၭ=2MZp܈wFlZmoXB CQOb׾DXkSێ/kq6#VPKtsTK+Au~km7ބ:òa,&q\$@ɯ׎K#*J2uvݧ3ǏH.b2e%kۚt=#*ctn^`$ Mu^Q`mclR5:<0yuvY<DB-źy~oғU^uDo3q" ;b0z߂.hth BmOrv@0(㍉KI¶[R9dkb(e$UuXFnhi4 LBc"Pd#*KG`/'8BZ@0\k#)"p#5E`spZ#+s pl(3"ȉ*+9Oox\TJjY _$#+Ժz2ߟG5.-yxw,)F8}zt oOGסQ̉'+Nx;}s鱭ٶk>IVssBiT,[g$ȵ4MF`{~>;Ͽ-Z'kjy\PN#)B1۹yy#*՝N'e:͟/_yG<xpp!`o]E(,DcBVD#*&j?`ǽa#)JؽYT*Br)S0e3_wsBO܇$AaAng͡FT*@]ja#`4E.`P-㞷۝ok[3sGZ0}SRfñ.Au q#+iSrk8*8BW_J0T6( k?3u]*=cڎ]1rA Z;"dT,+.'P%х\K59=M@e#*C\73O =:$A7g.wKj]q|t1f wՁUE]-Go==Cd_C. Y@#)`tk_?Wr0kmd#*P6}unT` ie ݠi#+vB1>m>StKKP}EjIz.^/C`xk\;&TZ4 A)rln#)#* #*n QX$ j<#+ǃ*CؾAV|4U!Nä+mښVij9(Kc]qXK" XYHq; tvHYlN鹱{N#*cr6I0ű Bt% w{qWM)'NY @ V6kM T(X# cN O#)@#+]k"M~ڎw겿~մđ#~X;ux!F2n÷#abߍbHzGFF#'#+0E#*%ArJ"kmb5j"H?br;)X`4U{5mGs?y&G&mfkBdٮl2!\"ty\MPg7;ϭ_s,uvN Gy6:$OnK,͆$'i='Y!as#+Rc$a%`u)]#)S%SEĹHsIh1zGz)KܵJG9jUG,Kϗfx6#.M{={ފg6=!FA#*ه<:LH;ޝ53ijlfIbQMC_o"d!_I<`''% .%9zlCۇ`Ez`|cd AL+b['#+K鵱g?Ccjz\[Be$#6)R<Aychng+5zV4iDPH(89H6(#)4N&F7yG2MY!?ІߩH4P 6㚯PWa9-E V#+Xdxk #)O,#*!n#+TgyL\9IA)8娦&fe"ٸq`v$+fF{fUXSQ#+D[.7~q9'g\3z$:'1`g7-"a@5H#+ rW@#*\?́UJIpV˚6k]7 #*DEAAUWFo;!QEhFhiZNA@ٚ|nKhjTfW)H5# $L&*e֦Yk5q2]Czկ5Ոhk[M3t#cFYF56ӯ[}jrѵDERLefnv#*ܷ<[t!;n'jfwďQ4@z#+.*00(E,nʸB)(@vGƞRhބCeK& dTE0\ %Y,l|&#6@SUެX0IA#))l>Pd:۷rhY+{I#Wy]:e%5&VKDux#*w6ޡn4saJ#+S_S/yX>p2{Sha,s|t应}Z6aY7箜fJnP.nS)QMݴ&a{w#)x;G<y޿_WQAg|Q:7#*Ԏ66Oa!8zu+po k.%G+p=xʩEIJR(oi#*qo5Bق5K#)J,H)|#v#*r#*+P é?#:@Z#+(e,OHE-N7ή k ܬpv=/uaG ,p§_ʉj#)"V?QB#19Y B?&JK>Z mDd\n>i=9e9{MdP"H}#*ws8{A|?cr?I8CeP}XxyC<#)#)@#*ϏjLIF zwy]ǣihBqGSi*o􎦈N] DYdR2>X9ε w 'Ƞg7;Z>Ht*?}]]%콅x_dW'>&X1UD bVnZ CU[ևL 1 <FI$ @&!V+ ]4d>ЯI>S;GW 35[S_B#*/T ߐ20U*Z3#*û#*|5 e_c$=#Cv#)u0q`8Ijoy99s0d$ہzLjb#Jp2nF!~2s$qrp`'G/Rq֡CJOg@<Dc *RqϛFcČnF=7;etԛ:y9L*<5L$EdPZ-hU_; r%"QC:KplV#*p:t#)N>0O:>|ޠ#)ٿd@<U{MÿKh )SjEx{wm~¥ b#*a,nOlgDMI9QGIB8rxib*ΛH#)obJRUj}]v<mlqLq="/דK}DoIzC[a?5=CgP%3F;OA2AE!PO|ٚ.8\X֨WQ$Nz@mr`%~w}x Ykw:0JPPZ8}%h"9BNɰ?&p5S*1sF7.WF%F%LV#)I0*ɜ K@9mUQ*̀n#+7(/`< 1R"QTv(&op~9k|4#c#cΥh,PMad'Qbr@)c)$S6'?jS3XQXJ#+Nd؍1HGG4-s=8گTmW/4UXS }wThYPX-Foy[\VL6T& ZUq*lajb'W<qp2ϳ)ӸPPo_ 2$Z)g7##*nĎA:ͯѨ#+lH&j X@FDa#+2 K8.p #)CtR}(⥅R:뿓xBJ<:Ny/H/!څ jѻunO>)'74=#)6:#)_fҍ#+<Nv9TMlsrEdͅ@a}VQ=yd:OLIsI LPQE%O=g̀iZ/wۂs|e6zPH_g$Mx*3&jB!(,H\`ɺ;kRZ51 fT<clI$}FGǍȁ6z#9哒ϫצ2 6)ʄ8& X>W̩-#7^h c% ~( !ICT<eƔ !W1´ww3Ԇ% UASKȴO|Ok #* #)PC2?8'Q EE%*G 8Sul2/a_=ڠJb*IR{\,q'TU, V) B)WH A8ӝ#+{l|tثH!B  fEq:D3O@?,_}rlޏA#)c#+FpHPLWQrNaz8yTi#$Y:#*)ya6.!XyzK*ü`pR(Mގ߃*|BUBP h q pNX?J3!_ӣKb=0:J@j1^/.o *T5UʇTdK 9lQ^aW,I*yPrGF|w#+mrT>UE:5,OZZ#*w#+Tsa%j:`|sx<9x`7#*6Og"w`PjI&Tp2߬rs3:W˦]Kgϧ;H%C'*wˀ>;@ rA[g+Lհ"􃚡EM`wA1!#*p #h"H@otۉB{P^= [66"N iUz-\N<gCٰ5#J B;}rTrZO!BuS(/bΌ>x̕l/lD$wQlPʟ__-:>4-ҀxBH",bgrx#TɍYD>8ydT**n %,"p~6(ϵ 噝l_Ύ7 Üw~՝o{ޜ u7kzs#*DD#+˲*m-h `>rB#*A#*!a#*lu84BG:R쏳WzY~6"g ,4:/.˺0,Bd%hAk:.Ywn}Z]ttU_; U2H?ᡑ?zVC쐡,`067#+/@zÁaBv%E+{{w}dP(A*JnEލepGٺn]C9Qp]q#*v˴[Q1<kCS#*J08"/zU1(~ nx<"2+wi_W;X}C C ."=='3pFP}Ծ޽#8&`wl?:8z/(AJ8g ILoz΋ma=j$@;]wwe}ؼ+!ku7mQ8cBaADBOoݮp]ИEΰ6J alU6 dcc.@M[:OōpOOBSA]OOD.?#0Y?cCU*9ոS^t$u#*8oOR)ߜp(MRxW{*Gr`өrd/!"C=;̻wu%#+j8$4̨g@:>\rU76$.w[S Zօh҉&/Ĉ#)a`d<o NgP]Q,EDcN_jd!ˏLcvfh@s{@]hD,}rVaorX}/Jr=B\~{aXC*^ޢW.1s!! [0 o,|]ejft̶#*#*]*EEFeRC,D%%D_hq=[VkAUE`<5030dz&ϥQf8M!C[1׈0~Q-1 *SVo Y\fP#*DRH2[hV&X`i o pKʁGOdfWp+ju'wQFH+\"z12'CdR_mN !xׯg<L!?PhRZ,BRTz?P[ϑkhI  ~ qA:彴jr4wNλ|aߦ:x~*=V21UegcN??2ON#9FC ;C#+r_잵VPq#) 1#+AE9{7Be6BIH+bH|? /n,nQ<XǓDRf{LA/!,'ɤ}/LC0|E:o#)淉M&Y&\{"m|<Ș*6<}|<=`͸_W&`ܘr 7Y h3k=[eHuX+óe̠ DgZx>ix|šky!->;2ı^'s"*St^lo< 3@k(P4Bx"sށ '*7槒Owa* 4/olr]lo:P=?Wnϫ1Bbᤌ*fv5à؄tx~JE8d_y%wBwsD#eўNѿPh8#+,j{(*]{anWYJ}GJ b0o~ qctWXUr}oÛzo&DY-8Bub8*b #)QA[T?9 =d|0(Cx:'IGGHNwS+-5k~W9E0{}#*%{U:V^B4xVscߜ @uTfkcD;!x[[Sufrҩi˃a:E8W+ç)#+QAE`wRwgyOխ&iLqfeκ`en؀Qz('ntZax_&z V*]0{fX..{pHYثmy׎00'w1#*h\ݶX@S.y~yel2ozkST!n" ELѠjJmz#s:Fe~@%qTl‚) P.lq\g]_I#)#) p^#TY^gߏTG&ʓfgc"#7wTdF괜ړsHY/<ϟ;w9OTg٘zB| #v&w(Q@ Vh39T,4ć#)[C !P@ا&j$E?UL~+Jȁ`P)@ %bx#GR6نiXO5(Պ1l}{Ҡ} dnTwͲ"/io5{mF#.̹#+"OcʯMB2d)~Yrr#)= \R&f.A )@!L:VA$)rRIǣzG3[Jn=B' F:~2t&fHG/;BdǒPvLdd^Hp1z<_ޛ;%Ky@DXvt!p}fbb!|X[[E%%Ov`9l=ug3ϋZbjD)={5e/Ca%Bqv`5$fEnnL/$ytOL\ 1VS#+äB>r/b{_#) s,Q` |\|=ihBФ+8_v4x)z49EJkuj"&lZ_}Ma//E5UVLq@sٷ8@u1`6 2߁`SP#)Qw#*#"1jEvjQ;I|r#*nu: #)\ kպu^ñLC@8j"Yq'r[ʢfN=3'>#<4_<Pw4G"Hbz#*8i|BHt\vﵷeTaWCX B56<OgJ>wWg# ,)'Cgsy~Qk|eBmcȆxe;+#*p}3ez&;1r"B&qd$ #*@ cX`DRAZ,<vz 4݄}g1V!{QiM5vi(E* @-iI-,aNa'#+x+@ n@sm #+uɬu^n(CwwYݸAq*$C v:URZ&Fqi"JdDAz#+C.2 0pD(ܚ&E~?fh6uyXyQAFݩ#+Wև(@H165 Pǻvp{{5I*D(9blDGwş0ؕU.J`pFΎC"`ÕrՈw<˃]$NP!Cvo$4m ڨ!0cKN5CSXȖvoMT0|D!ɍJ?9Myh0<@o25n@"j^ ns8Gln+ +Tp*v hGvG-2g sck.̇xԩh)&D0aBUj#җ:&e%Ds.5Ĭ^r)g[b.;a3X8: 7o<(y󷮷v9@Ruǎy[o-YClf|^sDR7ckfk#+2p$BVi-n퓤`UPaD ս܁,x<\ئfX=qm`& hl#*nX r:XXM}n>)d3#)a8wIӶ={ow !a*Z|.65s<JhFfފQEAyxO#*Sw@#*^j _ieQB b֦HAzi k5lL㳜H ES-ΧMPak)5D&ap+J=+lBuE^»bhloo? &6LEN`0IDWxa;X(`YXj60҃$9gv1N!lzgUQ#+ P6{"!WfD'afv  c =t "Z[}zuR˘#)rxd3U0P$#+Oa)Dp7w7Mo 6-KܘSPȸBH$=aɳ~#+4Ύ<}|Mt5,8Nrٟ6sTUY) sa#*=3z;B8<*]-Sv{O#BMp/ 2]چhtPEQ*4po&&r%j˪Ĕ07)#*ײ=sF!g&(P(X8LoEu3XMHI xFzy]N:9p ~o-fSlUeǙ2ffX12%X.5[Zm#*;qg]wNbiRqD<8ZK#+l7#)B6;쮱C/|铮e((*q@^zsϦ&#x2S#*3=FChYCS8G%kQvf92uE+]s% v|%qt:Jc輾JAX˸-#Vy9ZE&F,YKb_Niύac{*؊qX.μM9$;r)ADF47 Q`PNs**á^bs!#*kUZJhw*E#+ Rڝ >Zw}"tv˃^YxZۯ_IWfQJxף{q#t:e9o&u-L!ZZNVxn4: ([F:sV(#+#*daÅZ#.\,X|i.d_1ΊhpQEq$532s]"zuhuLZ+[^"H_E{_^Y-aLn1ù6W&̒ c90ILhs>#+`HA-DZa#*\'#)R4PjxIҖiknXaۻG-9!r=lrTq8 v;= QL_n+5E!T`yXw4Aua#*!DCs#*5T pTPt!Ma!}bI#*qɁp#*fMQCJoH^陖XKa#+ ԩcHs-5^:FuF nܷ'b<,!ьS5Q\Rw|j#Zq;v#)cDxM]Q4٪f(G1ЄH.,85IF+:5_`d{S_OK[3z#+tusYe+\\g؇"2E3 ޫHʂOJ{ $1ZB!B@&9͗c|<Y^~166l tV_VNכ˭<ސƐ0\`#)1PCnW(#**&`%mĨ G@PIyѻ#0LܦdYܯYTS}kl奵NZMlug5>l:#' 8*xd;D#Ϋ#+d#+X/ef3+я6#*MZ8g_fzy AJR]EDibѢa #*;!H=(Q6o!UJJP#+ !`ʑ=kӊyw+#+O׆z_fY?8tEdrP#)%/؄]?[Zvٛ&Qj&:>Ϸm̔#*Cg?itӥCĚRT5JDĭZHŁDBSaJ7JUơphۍP?{-X+>*T*ck(%n xwsH`{!!%-zw>I#*P*A >;iDGN-#)ߦ;,rIo^x)="%Ԛ#)aIf]ؐKd4~;H#r"*X4GZ#*ʛäA=0"A >|/ R韴z5(ؐ-hF)w]cTIʝ{m|#%Xtkp`]0D[YQᗾ3dvxHInbt:2Wy{C#)X0 zLΠ~}P6T<&zk7VץwEdwin\i/E՘(x@* #)4d2mcj墩i,TQC /킀ȊH""" #))zmKnS#+;eQB6E KVlUFւnb%Ȱ#) ݁uy^YWe5xע$xpMnk(ݠ6<o7LwrnY++G$Mh%+F2 L>6|,rl@$~=$.4e ET`{ OfՓ'!KQ8!G_r= yib^-8f|HH+]TѤt)mQ#)I$/,V#*_=}Z4w90Uomۚ4Xif,&t9YؼG#)N~`?՚ξޜ9<h\˕]awD$ -1( hY<!Pp2DVb4S{XR\l@1"u@;G㕪Go ]GwⷔMHM~Fu'1?۸U{`K/p]g^I gkCEW; ݫ.#+x=%rjۈ<͞?nĩ'z<[JA6?Iaw1Kc(Q(9?BUJE'g_9NEbzi5Rt'!W" 5";YE*t+e+):cbQXs-PBNڄU-6&n~5[Ř̑qU DvB422n*@]t$ 5%Z-٫I*1XM6i%lkImdeF)#)V1~_йmn<j}P;XI W|#)#)`;>öd[&UvBCc*j 21aYF6(~#)VlFTHmkܢVHcQ$4hx&4_*J2-#)0_@/d 5^=OlQQv)-v|5#)zNd[mK#* IU{X ζӌڹͯ>sE,#)X~6$#*H@`~ykIj+W6\3aJES0j*Mlm!(~m{wWޖr8@#) HAdAN#)5(HwI=8*i|F\"ndxW'h=b@OB4OC/؀p|#*B">! ]O\zjm#)H0cu!h Qˆ?93<zitӌ iyD9!6}ZitGM0?ZOve%neK!oIv9'+A#*Zb i1픉;+.9!N}P_ۮo>*NK2~G[P>X QC .ozSu%ٸ6E42T]%rNEX78#+^'I byg@"O#)bcFʇ}GU|>/ׁtt#+ Ę?|#1A>W<Q(hvŮ_#*9W#*gNٳȽ˟0ǭ#)?":#)f8Ȼ]H{w^ P,\:J f2S>bFzbr؝Zq5 #+P NC>+3wf!t2B"b33u(7CĀ#)B" )**;f'ήGEfnCD,?2ƚ*#*(D>އ‌WwkCEX_tP Ebx:/ˣpMPb7qBUjY#)<E?άpaLxGCE#)d8H#)E&F|밲@rٌ5@57u2 T;w?uedŠwwň!K/޵ďC!m01Y6&#*#+Yβ3//23q0-sitoeZqێ}kߎ\.(]Ye4%B0.Hҹغ:qfEE07~6ٮ2.H#+a`)'gNFڃM 3l#+vl`۾}F&h(t؏˿;j4L)>0.fmQ}t*;#*|!Gç=ӎ#)ɍ7>wa{3N8N\uÙ*C{#+~YrBYa b3H)}F7kեǁb8 Ja^XR잠r@vLxztuD+A*)gh&ܕ0b7PTFFQ8o%Ƶ>kv(M!;2#+!#)FB@w ZӰk2p#*4n;ulK lKr&Jfv5w&8WmomR"Lyt6Y7,Ln.-]A}"CPd^DZ,AFs$L94OVD`vi,nOmcc"Yq/7@uc/>ӷN۴Luzti9ML!vkl1,%aoq.¾\Mq(|<iF#+rn:驩GȎ漉qB'}sTx%'4f뮹gw l6Y;6DdSW/WV΢A|C^̚׹@i}tYT`#+b͒se'uu6r.KiKkyruڼQFBXQ(nY)v{.cewl,E"#):7\0mIjk-|Z1#)B$4|,`unV;~JpK(ԅ X#$Pzn p#+@"<#)&!{Sخt$N I@g./#*iA/cah1PSׄBMe>5H}WvC0_#)bn*F[fUnۦ<vܑNHOu2]kxnE.Ȁ"TcM7BJb#)#* @$K#+Qcj#*KBC{IcֈQptLX6UZh:-WEFzp!1p>Be١͎MglF2uE#)K:S-bWm)c[bfUT7&a&"A#)'\= Yx)ۋ0(FBgrB '9=z}]oXdMS6Vh[hֹ1T?hN (Kd*Ex֙1Uh1Ez#fCDň$X=h,yj󫤮U1UL gĒ5 >"qIy9o$B3{@]k[j=cj7kgKm&ܹ.Sɸs4 JApN:lEƭm2L?]pYԡR}A}!9Φ(CvA />!!hl5Vd)p.NE# кXX)sC n@d#*sV r ( HWÃ>4Dg1 $~̰$#)#+A̬ hk7Y LBFWS#*#*0dL0Pqgֈ="ɘj8NXRzA#*``(3S@FQ uu˯.,0ny~sxB&}$qMEdN#*y@TbrȾLk 3O3pd١9#)PNή|by֋Ȳ:;Զ6] WdǦOi@,I#*[6q>r.0\3kz(dܝf_m~X#*)Qc'HC-&#+TDhvOidAsFZ%α0g,#+Nw3%@{no,n< πh{*^#+c>MÿaTZac$/ \)(DO֔#+ yh~ b#+1#*A+$t1'?]& P#*"\PAqz(ғl&7e =ZFtHF}Z0"'?=4ķQClV}Gd GH#)Tjn(2^ԕZ@yF):6t:Gka=8fY]rmUȒ^X>w#+IT,Q2xQ#+\zHy!wD0Ǖ-겑#M$g!S`b>b1X2EH,ckmy3OFkصj#+2j!FNCY)hS`6cHϺpTc *aX[*Ee; mc#){Zv#)ƭ$5ُWL fI ٹwXU0Hzq"LYq?͵;D6C;#BT&嘌#*ؿƛ Î&C'Xn@vEE#*b6HB 2&.A#P;ZvM˒rZ@ر,\/ĥ qJDMCsAAa% fnh4Q(Q#*Fa#+a&dl;7-2tp#+ 1h E2V"""X0dLU)hn8Q4-" X 1fȭ[&fVI]sYML5# I^LƭΠ7CJ=9t8gf ˁ_;*1h 'bG+\{HƉ#** @R #)ٰk/I0GØ̺>|ƥhYJ)r! .6XD.=ZΈ68# >eр44-Wt^agtzdMtS[w /4vf]60TSB(N5gAF]!&|#+.{+Empd6&M+&Rh9jwpD -0>-D9Kg#*B;ün ~m9ggähAdN9XߛN|ߣB !DDE%#*&3ҒCK0#)F$I :$sMeX<5\lda#)!y6:1@i5i$#) jM%#*7&&Y=5{Q#{ /NR@Iu9Y^|6b]*#)4&̢&Z+NNr?#+e),̸b;}< @Tnwx<>xHB~eA`VEW-[cZڊڹȀ2(A!CB<5__#+):6۹W*@33p:K<C<u6HED!$hILfYRLRjINZ1Q4ԡ"PZX׿nPɭ&,LɉfHTPĥC#i,)%%0AD(ɒ5Dd)24iLcIM)ܓyS#+{'t{?++UW(}ٍ|?uّ;!;8H*9>s#*#*SrYqS(hޙ #)#37ZuKEF>6%vۋm%A<,]\vidZƗCZF|w.vc n6_`?YB~+6& pʦAJZ^oůbc-1Y6#d(a;3çQ跍bLQ43ٟc7aAfJg9 W8Ć62i\[/czCH `MWSaO=G"!vNJ/4J|,ܸϞr%܏}bD!UVm\jQJA(U$I#)ؙ1[WWpL~ݶYw1R(2F^#*>p}1c"$o7|^ZܞrXLg/.>^?#+5#*j*[iӇe*t*.#+mf8XP†Vڐ(il_zРQgr IWŭ` L|dj:rߡe8yacniq=xKC ͮyap& EJz3\{e%+ii(P?K GdpNB"T]SbY=P#)ƭu;/N6m̽"lG9vefSnIr'SV5:W]v>OA8}_EqAi9*HgtwԜ<!yCseZs9Z{K(;^禷;{B{_;Fp"aQOJ RGkBkbl8gm]%|;]Ȓ*&Ps3 j5'2;Cq#+;<2 'XgE%p@kṠl5|*=W_ʳKh#hc#+Z7o6̚Œu56qFXnLQ桦eiiKd hѨ0Wb$YB,.7:fڊzsְ'|JҔwSU/^,3e7Q~ɇ[#)Dg 28?7V̶9ѩpiy+;Cs}j#*.S06{P#Orf+%gQz C#*CpK fē:l.v0dA:o,7#+UEAb Yi疙GA˜>#*Y5V\0>x$IHݘ5ΙwJՆBh`X;!67LjL =_mb.Pq#Z'=嘤 :5% ܚ 0{01lGjij%\X5vxͽ4V 4ZMDx<Y0g;iʼbLj-Y5ɜ^W7N#:LNvY6P<zmkHpbo܄äCDY}֫hNy(#;cCHMz\j9ow1Mbsz!zBT;AJ>#ccYwn(}q|oWa3:;1d9LJn5͚']ߗQt\P##)^N:82%pd"^!\(2CAl(jS }5\7HIl^mK a+a[*ɾU쌦!2(1 ٞY5d 7s%}.oͧ)8RcLܐcZX!L2@&hCI5$mwH0냦`\%Cj3f HA А#č5ND#*@\@;a#+i[T싇ZN#)]`rmepn%>PiI#+46Ijv0,iQ!XlhpNVNځ]#* QH.q.bJ2E 6zr'i3jR}huhf,GmHBfY|c90B P!j1%EN74BbeӟK7mppRj#*'5xs/ps[Dr1QvkwgJ]qEBIB H9amoaq&SLMx;dv#*o!Ձ8fmqKfs%O6;!,HD#+%'E@IQ(Qn\11yxƧ#+3[TBUqxk ZG-,ʃy.vX̼!$UH-G5fJ1[s2B9H4q:jn21 O)&PmV\[lf5,yULb0vBW5Y֙fB0 +.2#+A1BP i:c=Xj8rUW8bvgN+X0N4a 9q{"aN+(0e#+Bu R`hÄE8($N3L#ItqFXrm\1&ԯcQE%*rK#*t1#>"<4[K5l^;6Z|g95{dmP)M#)$̠0oj9ّ4tP)QII@x i4JLٝTwdZ>h!#)4BX#+_,nۦP&R ˰dYٰܱ>!W<Dw6"d^Dk¡H1Rd-XZvCϤ0Cgys( A1 ej7) aD@J lͪviZN(XS7pͤC a9Zf)!윒ZEO7"eҤ<0j֥:3T#*J\sCU xeV''q#+X@N"g¨ c8zf\#)CP16`uFjlA;Hwn˗۶L>8跎Hy}hn|Jfn#+e󍙍>tCʡW4T΅J.cvk\8RҬΠ|`5`pG5DLb]CC[.an!!aAWɄӌPiI+]gw62ǭf 9i7U@8806Dr+xW\+rkǦ-N\)``iڐ蒐A.NGIF1Hcfx˕cik#*F %DvJlRQ]+y"9a48yr26(kXml.@!lykCȡ#*F+2fis l3YU)Q#)e2ɕ%d#)rṙ#+9Ň32ƎpUC=NA#*BÙp+q%ʁh2\f$#*DN4;1͋ F94Sld̞)Iy#pt( q3a4nf:ͫt5a23iRR8Ƌv={fͨss!Dw[p #)C@#)búgTđb% P?IMDT)rR(#_GǼ= I$IddBO}QcQ"'T:FԑBl@Q(#+@D3#)P.H;ߖρip酂INUI>܍VCuu v,ťkFw-QE܋h^uL4&Rc'j_XGxΜkW g``f!xF##<f-[,pCp"o ŵRnK A#pB: 4*DQ*4kjIQ#+"PT! &) 2Щ 4B,'^8(ɥgp2C~u6|٧b *EUnR Z9uwu]26J槗[vK#)\vcTw;7#)$3C}C̡8jQaGS`=?Htsב}+&EA-`'c̾B6zfiZLPOB0@cK.õ6x e Lfk;[&dBboQ*4#+:\sqvΑs.7 #4"k;ϵ"Û񷇀\b^z#o$57=~_bMIL*ѵj$Ƣ&IfDQm$Be|džhԧ#*$2]v@YC摢#؈Ad#L#+$Ej;HmUqyx:osש2i;kD{5|)KXQs>5a$'-ZDW@Hm<23+i0Ʌ#H7GEi1d#Bn^ʺU|zބj#+.Ɍ[2b+aZ\34B1c@s23cw,!hFX#+D]Dh8Y (I@U~X8M;&Ty;byޥ\7j." C} c* 65$*G/z.G5D; tf$^&А6fjL*qLuquELhF1IoGTK&Bf:ql? #)=Iz{J(*-!VDS04x#F%ƅ*U#*D30c#*4$!|Kxz[4g<0F:N%E`֙7pQr0s|## qe]cCNHgDҘo8wh"4TuL4AѴ-ㅥjAl"$2kBTLH#+gN6:Ů(lYM{^ZH8[3<#)UehNå=(JvnN<mmP%* UeYN9#+)a:@mQ'u=ppSɱ4\CbFq=}l7;VƄubŽl El0,SFQuv03[)ex#*aL*d@!L@*#* #)_z-$BE,$:bm7kB ?PWruH2gޮSN)XQ#+4@.y_#+zfcN& @QhWYLL#){澦H`!$1Mk^6zŶUj-m "#+H$ Cܦh|^Zܹz>#+ˑ>!5#*-hq\Mov΍^AY[ QYr$E#*{9Y%leʵS0:2Kl4ivlt2VV1Flc6h9qH.KӃƶ> s7a 6WIT%/4嫈DSNDIR7DM̗CsF7ʚ1NͣqNv˧~Q&؈h5)a=`C8ۧ E2±J|&-M 315ę3fgSbiyXzMp\J}d3}xdǷW!2dž9l4(wMc&U/dآsLggjRP@Æc$"1a9 czպR,"`L1`mYG]qK%!##+\P 0hHhmQ "D"5P@@`~ V,0ܝqJԃؐ"8qUv;bMI#VeH!V75t*i5t)7]L<wWbcJMn4H,)806xmʤ"l6ƴԊfVRm,ʚ45j60X-[I B*BzH:#+cV@YKj@D8Ed0 (TM ؔDACAnDvA{,=$# r,msj'>b @a赬fn51b Hh$̕5,aii۝C6O}od@\b*FB,Z2"S F;iwo.kGe]}y,n#)ת 2!MiK٘ˆ<LEz0"@Ѫsdxcj.\*;6zipC~g%T2r "ˎ-#r9,!3`ZI!!k**k8噧&(\ :a%e;N_#6Y fK[Z8&ajt'wMȠ3 (}JD#)"v5e*̣_](:~BIBv{[VZVH]L12IE25bZie-FŤE4%JP)F~EڳLfi6*֩zqEdVw]6/i^ݻ*b(2#+fҌ֔1iUU־"XݲMElD&ٵ˭-LIm)Q-6(L¦6[6{:mb<mYuurJUM#*rYJ]fXHxMvM 0E#$eViˁ|+-,|;+զj4 d٪-Dh2|R"bOk=NZe,Xe׍#)jIDtKM6ԤHro#*CɀQE$$T*QsbB%@jGu)mW65k}1қ#+jRdj+e6%-3E E&KmiTͬiMM+rIIA(4Ғhm6SH4l4ehV)bKBƤڔ#*EJlJdJIT6ڋYв2RcAIJddfmVDRdffdѥ$, %R#+@`4M-ZSUe ,#)v 2"R-ڴU"´m?Puq: 2:B}o5pbu9J&'qMt#*GxR<r7RB,8 gE}<tT$1g ~6ιų@f ivz.Mv H+ѽ7e;Ґsq!~FD2lm \cha!#)SsLot%)uB + yTE p}Uta)ːRu#+*W)A=|5Hqf.3#*L}6dXs1"$]n{&olˡ=87](p]H<Ĺ2RedBS1*LPT_P.F)iY`'qg#*`EFtM*CzX^7:ߣjSI|k}ٮFrp[B\<H LUU"!˸#)#)V-w]!pvVyFT Zɴ؜pciVUD`SH#)Y;+s8efwm}ٲweptcoM>WhJ~=;6@=ں6v#*G][{LcښPP,;n !&'xQ\̟y^)6#*iXVYlQ2$QhќRh4C D6D5$-p#*9m&R(7#Jh\#*ch7#*KT3 l Leq,TCD.XUTƺ]$ *Z<R)0ΡHkF*LHM\z5J<BGB)55N ҃ā҃4CR4j#+pv]-"j8s*Wgq'N\$w0'M;}z"ط7;#+@TFAj#+#+_gmo?Z٩rH|~p U7p۵}NgB O>1Mnx$WUc^o#M! yh^ QÛokٌ4!L }IfzIYS*#oӽ#*C{c2>HT#J)X`hb!EXR$S$>]Z<._; 8RN0$ S >&¶m%ümji&4jRRyzj"@2>8#*>w8篌1vb]00PȵH =G"3Tmpܡ\}jsfF'`aZt0Qii}'S~UD=9J^P'e\fz:qyD#) NbCJ&&@C8DϾw~>gک47uEi΁`sCg|#*#)Q0jH¹(7׎74Lmu#MCgӗ@&MD#*Ӓ5Ռ-8vI9:bˑ(K!1€8rUb*^zv3VT.4;3sΌ9 b*,R)`f"DB *zIJXB%9FCqpGH]iHtGV#+ $iU8@b# ꢱ$\YuJIKNzszLo;؃S>5&#*HR*ZDI4uԛ3X|D$ "de<s.W[rY<Axkz' "#)$l`1 vE%0Xpe2Q0{݄'C[ћc[J;Pa*CZ&=#m>[tu࣯֭ Y8,ז)B -XLT,KԘ8R6y:cّi]PA:B'n3|s+tF,zvlt-~} -$@U&j*h9;%D(YzwܰjN,0⩂e캒E֗zr7RmdL$'z^^tc7mww@5*Ha(QcO;˘씼UCV(iH@M6;"说h4z&cvD RMpuHwf=5~/k]f"~tZkZhՓjP:U2df`mEouM4m(֫iTI蹷CYh*܌Xѭ|iEUz^ּnZ6ɫikm::by) *p%\TMH#*K8 BЊR2DZq1'+C oЂM2PrHD}:Y4wfa#*r5lm؛= I>l#*hov^J*_r-0v&ଅ!mOʩTe҂WK$2͆Ypt!U#+#Q#+DUKL&Tju۪f^#k*7/a7 f{+ab( dYQy8ൢ:u-Ɔ#),I)M3#)vD[BNgٌ(4dA(蚁CϮmwQ,#*A {k Cb%58\4jm9Gk DT]dn&ߥ=ŇwuyBU?oG }/?$D'&\^ọBn͟C!ϼP:͍؃#__;^H$PRID #+J*#+DUYXDPPflYx@ ,%faNk3.?ކ=3R0V(,i,X)6B\r;>SSQ~ݫj D@$SОģCN/PlV_$PewGjBR+Do,11Yzqa8X8j܉8:Ӝda#uA5m,W5Xi.̷ܤN*^D"ta5ٰTY*6.j,?6a&uCF QV$6ky Dj6daXZ? Q֏){~]jiRD@]C*H0Pr5u8bEXwγ##*fQ"#+RfXj+H46fP)11#*YTdEa)VkBS ,H¶)&Y#+p2SPa DX!l #*m&46!FFs!0! -&h t 7 6ȫ#+p´XLrt5{{wvwnksV6+Q\/xĘ׮K.odI4\3lApjӴK&Di:3ui4Z1(\'(2Ƥ#*0%I@ 8ZԬ<5MSX3b]W?4}0A,m*[lʤZxpu>KϦ4onnMnq1 ǞVqz~#*ݣ.H( MT?< 0X1 EDF5*Y-Uvƕd[,k\WRm7[dX% YPu#)IIn>aXvj ,b$"?y}_A`5~UX鮿w}XQc/rCCPbPPBT`T"P*(R$ 0 :njĀ+q3-" <MP}!UD$I~X) Ũ5#*!,mn*YUs[m,Ȋ-ŶUGXצkyWɭyjwQERMjMZ4K߰`iI#)#+Dt dyKR9uCE$+ܢQJW#+.I#)}@!x!&Xb6 b"ٙ R;#)&@ʟGQ#b0d0AmCrÊؤa=#) .=Y= oiL8`1*E*g/nԈSN#EB݅MC^$`1v rÿCdg W+0bAJb#)h|&W28܋C{WFJ4*j kWli5+(F0XDds2ӷ}v@G(P] `>^8ʵQ~'G<nѐ캓zh4hATqp<sf$1#*k#+CT0f#*I#(ecm#&Č0aCAw<ޮت$IhLUGs'#y绖z-cW*,NPYT.ZjB̟@YA?B3u\9#QCS&>^J8V "ujS6ak907Mrb&'D0#*dIP0l0FfjRFy#+!l i=5$wL\jfbvC<ZK&$Hnd&l^zŒgR}Ϙ:z#)CVr̔>?#)8 $j<#)j4["ك RRdLŵ5&ƴdkev DʙignmjlE 3X%lS5T6̵m"-(M46UEͦ}Wf&N~EӖl$"jp+#ֺۤj]Mk\#+£רIi'@6Ѳ6s6(崷ꐒ ]^FBD2$N80) 0ه|1q@a>VG{1jC~ N'( %J whzӡ#*Q!(dI-$$)#+@nAE"#+T<BsEc>3=\ׂ#)% S">ʥm#*%"M\˨<RкD+J'`4=#VNOy9Wwj2j6x#)!֡p[5vAՂo/ Ǖ7# Hpm`q>#*S9m+.!Fb+BCCD6LмLӰ,X˕;:>1J=h} ~nkIP)ItSBe 's|a#)p -A箐_ϑъQ7oE/6 p)ߑ3')&#+Y A ,S4]P&云keeI߅׬z_;#*xoMt#)O 1 򢁴!n[$#M0 )#m$_/w )b`HE)*#ӲvDIUV@Z-Qzal 5Ov= 5#)ٓ=UZH_#),H BʆPjp jETq14s#D>i@ L`)v@Ge5ΰ3ɗI#+53@!duhA -͏--14X dxa,H2:#+0ҁ 0B4#*K ) NMxvi=>~(:>0C.j]$N$dG !ETBG|bOb~)]fѷÄ!,4FDZ&NAB`5ݮltf,'e[_e緶ƽ#j0QSASR03RBnFP,Kح^gDILy#o" $M[QR]o((|q"!Ī(Y]iu#|-0"`dKbz $mqII2053-S.80iq*JʖW"Ka'nk#)DEOy$($4#*q[BqK ]FCl &Me֔ je 5VWE[$FBq?52wbhGrE<3U6D]_1"bv'P e;deg͝Rħλ#4=*v8ZmP^OMWMs(@d'/m1 ,'=QC0ֳBo~[5PPZcyqURE^u4^3b(ޖ)DTqa[PxAOi#),H#)vgom0WWۮ05j( 2#)!GHGR:Dz`JX#*@\#)q8R`h#*}]^Hjۼ2jY8}ޗVI M B$ƒbjĈ&MmRmZTH2c,@=Bd?vi|%Bn%I -[CbCCU-0 3U;i]=Mұi Ʊ4 I^e^5%^;WKj#+-20,f(E΍gr/E!cLL9x5bP#Xed9di%<y#hH$lx0@#*9B,\*ii@e3lb8wn_\\;*gjI KU8#)ʘˊ| 6'!>M-.rg^Cu ';IÃ8'Iϭ#)}t%u@1$D8 yK*5&Z6KIcT~VLU=lX[$)#+a 0?OBzKe(j8 UXz;-fC5"ˁFˊ׮|7ׂ02W;n|<UoUx$#*kȶ #*Iعa$8UAvS&lw?͹)H"7 #r)1{l.op0B1VFy#c7CD0I%:EGs|AAo-/3m]'щ)MA=gK.Ź6H_:m֎=;v9@xF?J@(/#+6eg9m7>G> iؼ`Rv@ !*nQvRջfMO6Bdm@8LZA) y~LM"&V\s!JDH@zp.Ɇӑ.ǬJ [#+yaB`*  {P'6G109_(G1>Na{{D-<j5m:mV\U#*$HH0QKACoỤݨ#*([ڹy~uAb ,Zk,D#&:ɒQ"ĐH0nlPHpM$PofK>E A!#'v#*sDc/ytTa_ 9&-V)7EOi˖8΃=q#*~hiѦ`5b$ &PoA%IT)1`F#w#Ij"hkXH咊HڇX&O>%2jlqqNF#rs*+AZܷWY4Քgnںd54W)vo,d#) #)d:0l( }~z+MzPSnk.EaQ,#+I=Ł?5`|~dd|؅_ۓcP&ڑA#+ڤsc ^lxl6%jC"jP4FXl=j$RZ`#4 g7N9S-VMdmLOjnW&VB~l>7 Q#)2}MBM=ZuJtywϩy'|_h#fQu:ea sl1Gk}8IDdP+ğ/Wo-ODSR$]'v5ÂkPX爱SX%DA&?9-`65 .Hy4#)jP{݋ BoQp<Au#*NP1hm0z#*VѿⱒKNØF\'J^EIP]Ո$uK39gpֳfxQ-cBb8L6dfM.ۊ`D/a#+b@!K4Utxb7axAӥgLeXP.zY)0(πgC >|. !;‰+"d̊aB;)=^ycK-L7"O譚xޥs,LWkC;9y^y[uvdlpIfmAk<h:݉(6cz0RR5|mV]2R6:bAa誄!裐fxrS4ST#)jqSKRPNx-XFXFhն4[ RkFXƵ*^5mʣmsY#fك]Ύev")CUz,.`!i <zmݿvMDpw>7N#+bMjFnYLgQn-T}jctW3d7MH0`yo#*u#1'qB~8}a0y+0#*HH^zd"{`{r}sdï mg;6Q80;qcd 9Ǧů!H(zh{+qQ@f?!Vb9HC!?E) jK~`&#*Iy$ wm١#+ X*J >$ד:.g"gMZT YlPWu WS X( Ua[#*aAN{-{g#)]!7#)8 2A#*U *B8{K=yztgf#!A@҅STU7I209ux/7D?qpkA k Tik"9qE8Qs,86i-2wCc8wF6bb,x3Ϟ m\YA~{#)^DàQ =t8x3jjgo QW<!uEFD NCYI MIڍBU<aRavw]#jffhh!#*ЗʃzY ( TP$Ie#p`48*j0bnVjHptkFTĨ$HrEiA К;[$1dOʮ!nfUDTD1jW7fV&Fy&T(#+v7[QAD#%[(L o##+a#++&YFkA4#)5+e+4%1RI&6,3$e+/]˭ri˚ u-nw4Pj}>͆ E#+YK"r#*uSl5X<'Z4AvX==Ycn#*dZ #*}Vj$qy&i@r1i(fC}`jqҟD^^X #+׭QHùYi6 tӛ3,ۺUY).Y A@1p!*$#+1#+E45-w%WȊ1n0-&`i%ehCVS1&;"f;jc ߻.q#Q7! nL@67v:VW ˉZԆH#*2M4tq(6C NyVu5H*@qiM(9yCKugbY#Vnmm8lHjƚE`h1U)b#*M$}&ށ v] 1̳?ф7#*0.a1_#)ǩsSb<4$wO.y~nl\IXWي-d1!fkD7\``HZ%Smir=S|qS^5 a9UEfT5p#*PMOF,bGI1DP#*Ы()e z9R AUnnnQh P4-[y*6TC H0c,%B1TAv D$ЀrӁϨT}C`奙a'44HjZ1?h4Q>Ͻoz4q=6uߡ I[]TXbjDפ=+F' 6#*=)doowIعq7M^O7ɫ;2)eYIN[{7tn(D$幙ru7<yp3G#*#+#*E~XFp6b 9;TXM$+[ rs;0D9 B]va&}&~Ҍn^ >GXg~{ϬO9..ݚ>tLa#܍)*"e!柟5 )NKl7<-6lCe~홦jθjRNC?S|pO|{!0AdȊaDl- u%>*GBY#)! ΚywwM;y[J⫳kpmRm]bmy.;s!ίQO XHJu]ݫԪ4Z[y6[7V4mm5^|Y\ٻ4aF=F9"]+b*Z(Q"4uک皩f^.!{,4H k^ֵye#*b) @Tj)jE]j)XfT[cQZck2hэ3T%D̘J(6@͍mWK;)@"g ~G!OU4%I"H)e;/OLx~?EӴp8#;(s/xI%M6h77z5Qfַ#+OzѦn#m;:JZۘsYnέiZ۳Y#)#+R-4}6!7eg"1&Y~%_t($D`b#*7`Ij #*ȴp#+a#*_GPm+lr50^5+)6!4o@vu\Z;HCq3訅]hʈ<`6l<Zƍ K1&~VX82&ă#+bX3ji8$ pMS(Qqu?߭! 51<t2bsV8U;"ǑC}scewo0<&1de"9.{miOPց'kF)[htKEH *Ӹ #*1.gE𓶅c,DRsfGa#+&6g-dž*CbSVK55QGP'75dlmF-TDB!0?A#+>mo$uQpA,[w+oei8coE @fꗺm]-kۋ3#+"؀b-8#DF-37lZRE=A!¶ު2LFɫjUu\D]"65NzM^[f2@I@qqH̠F#*[F٫Rmf* 0#+"J#+#+ceG,B0DdА%aј@' x8 _ xgb1/+=]?'õX*x3&mYvgd$k%۷OnhUN$E#);KHFI$$ h`UH Oǰ<3> tPG;Q?<!l5@TFQxEs#*7d<2qO_}͓ON?,|:9hTO@Zu:d.Hв(?IF[.^2#)1Qg#*p#+j8Is /)CpQ.0ъ'A7g\#n=#+x8ح7̢l]"P)ZE&#) yZirL&`!O #)3R< 7S֩>ʛ&6#mѣ N3*zQCL_!eAwsmj]F!Ҕ1" gJr;S6|.;#*#+bD!Q Bt?w>,Yi^ygy\2嫆uf"i$l&ʖ۴%(l` %͹\^M]v9й]ݺEroJl̷W.,hv6V;mnZdJXr2d[tΚZ39WlTZ4ri-_y^j#),*=P #*; D!#)zw@g b0#)vC#P A;al@ExjoNyx~{ASq)| o=E=&!p(P&Ө`#)7 2#ըfi}6r[b )T\QEc#)L?o&5jjХ&a>I #** +WjuXC"Bбc 1<B%%ZBY(0vm$^!#x#) J#*L`)#+5zk[zV@ґfeQ@LF* 12#+SAJμD20ЉbJ!뚏]`5C5qw#)\Kb+R͖iJ1`ކ"&ܾEt:f5fԍ-2fʡ$Q #)<X&5#)$vj(Ԗ&Q@#X.8":dk33T]`#NqS]wM@QٓQ3egm=@ֆ0Oz3?^W}wդ#)XqT6cȐ34 ;Y04'h04gF6"H$8C]a~!y,r<-%CM>Z#)#*IY%#+U„"mA##)l(DtR]0KDdK#+A`Fi&ŭ*6VjBTE+k3PƆ٘\.RH AL R$Љ! @X(&eIvlI$T8‚OoPʫ3h} H\U3*\c@zyd4 #*7<'W5R^#*- ȈHqR@(!]!lnq)7n4}#wIRBB@WtFЦ咐 z XLviQHm-PFkxyӱ΁G#Ihպ#*#)%- X#) *@t* Cm{Ҿi]Zvͭ#PZtExO\My'K(Hz(?ܶnkؘ@sj'k*mpEg/<5괹JԷyo!,&8(BIa]f<dTL1}#)0'F>-A*ʫm+Xim9/'zaaH>6 邠?Dv#u|%Dtop dP.ҵinʪKKsT{BhsJlo#)qoKS;i[zN&GciI4A,3EE`>"jܣ=<}#+J:.(+ EGoᢘ eeta>75 /#*Pf#*IZ[{&6ŶT)y#*NՍ* VȺx$LdUC#*xYLm@iq)37#*D#)adƍ-!c?;CqsbtdVެW dSٜc$}%9<fHSrT`0>UNl "^ߊ%Ͼ#昦>YmVЅ,.]oF3qN8L- a}d,Y<c2H#*#*0oPŹk=i4bs '4e*,sN#H,I8h-0Qsz&O4{֞m#)B vRMѼ-?Nj}v)io㤮< nM^YE}\5e@æjm!%Hna17-a^glOb*) wQ%q-W$G-u4B!@6Rz"f)#*];z>C1vU/OU n*~D"йgA\}yȿ6&hlsҒ& ^ |L5I2lo3 A\0COL襨$D3MAF{k<3r?nr}l<X+5 `~I5j阷JI !0$Deh#*h$mY ϨH#=%$3XUSGRggYF`IY_=># e߽ׄr- Tؾa:6p&P{ wh|Qc!i@RdNAV)GJ-tfuw-*zf-paG'S⛄>?#+R ܁ѹmQv0/n83'=1K)`ĖȃƧN9#*T!1]OCJ z1SG#+,62RopiB#0^:LblJà bA$_ #)$PG!G/` $$""@ Cqz~X*G2pz cCQNq#)s#*An#-GCs.6c;7Ǿq1qq.tr Sյmh<4kfDW@~9({5BMJu;ǶH@wQ`DGK >X G1ci>b"v`U*^yH0^:=#)IKB#)1XH sZ9+fee$^~zdz[-@'ӗ`i! JP<Ao=&Ł#9q.JO%~Қ5ufvn}:rCq3Cus8d#*1a(G"tB.;!W<:+_5#*<GIk+ش#+WB_o\7$}lΊv6;;˅(:NbXê& xt.9B?!(6>ų?}«Ln39/.);̩D{%[џPAo_]HwLZ;u55(tЇ;R:R:eT@#v<7e1*2,Ε;r~j2ZJ,JrWtU&b-[5jkҕj6d#**ԒD7!й\u(F(zZ#)E9`g?O#)FeRDh(&Dj"%JHIdP$]'6 'ȋ^d;z㷇6]yRMv̠0h5$lAQR0%o[=4 8oG4߇+k09vg\vru{R2L(Jِ11ޗ `-MU_@i0bVǠ-Mci㊇U'nk٤ecjTCf[ri#+kk5I"X@ɗo猝TMp$ -e*JVi(KBjEIj0 Qi@FQU;JW+ntd#IJ4f C#+1&1;`f#+nJ.0ɔ6\P2#)(PMixW.,5dFr>\fT#+EHSH"Ogi-knqH܃oͨsp!4\4E¿g<e8tHL%BgFK6*:m1bCb%m`tDy>m#Iy`ppln8F11!9Z4c#*'mܲ5#)vjH-M#* @JrSM#+a㐎(Ymۉc"JibnD=#*bUDV).Qلm6*НaDE!P1Cie(lo0QZcaUJ0lCezOv9*A)V(SEĴj @ŏѩŶeތcvCg@TF4p֔ԃqs`UƫA@2AՌ#V M3OK .U׀AǕbbJ ,|u=7jdtEƶpf3fIkRPn rnbZ@<(ӄ}dU*8 ` w3N XդT`hdJt.h"dj!B!SUPj8Ό@Ɍ3@FoЈ/#XGdm5&Q@ݤ*W:22dI@PdIH*(h#+Й BA(0XѽGUtn\LA]IqЩDYcYR }#*<6 `᪢8A6( =M8@ ) qen#*nOdnDDayQ]}XPXѐܥ7j"B$5fl7Mrg +eF"&&8xt$Z!LaMHZ#*(!BQ!bJ"524 K7 )"#)8Zhp P;zR%R%Z+wv?ytŨkutur;rV;|-WJ j-(Z6ɓV6B!X.Gn45*v2,PݜԠY鐒nL銤jC;(Zr'Y "#)P1F"krAԤyt/}^T$IB\*dH0,痚.P0#+;+$(`fH؛;b1komu҇w `wJ .P0n@bƒ֪Q)2CYrT PjnkeF()fC B$p#+.<pd͵61.X QnO͊o#*o ;#*/=IEJ͞fA  ^#L_Hpm:/W~^P5R@[Ery%C˂PTP'BJρ܃MI}[^`z.Qrf,C^&پ9TkCOLKgueڂ7~#))%#)Ӗ2ai|DGޙAKed"Iy#*EP@BtHY|,fcn& N.=FX,:V*m(z PTZ-:#+@'?`{*y\Pȇ`;b\؂eb 0NRQmTޤQUsk"U Laƴj樤0mEMg#*Α9H<\fɱ&ě#m1\WHFɽ+d{5b[wV-pTk9[5d55\USݮEmA5W"5AKpD8É7cu=#*Q_g@QZ4׌ę6 ][7RD"#D@<QV҇,D@I;o8/#~ |}ȇ"Ё6I,|D:#+E#)v<UL) Q"FP74 0'"0Xdk 0#)6mjIX y&B]ނ-YFY#Fl "D,(B?#)&InZX0/CE!+ʕ<kJ#+1 KQ&e$ wڕt:҇unXKݮߛm]m_KS3dRZY#-MRQX6#+V#j[ڍiS*V-h2ГBa*$ cnC\M/]zTyC)`G po27b4yR@TҀ"Q1n6FAX1$ABl-0RBV!" P!#)-u4_!Pj,`ş_pAm)h@'.jnٍí>vASm("!bB chC@M%^B3`N"vTsm㺲הQRN {AD! ۇllkTE&˜SG]0􆆀\Z#\bl2 (!mqQ#+@|k.1b>flQjG4{Sv!ܩ#*k;Ǚ˩R/]rѵhք$@!pil@z%3GQ1TUwp̤BGtK*Pto=Kao~XTٲ-?dW<`x7)Kբ4/!ժ0#+Y*07ɫ'șૌ*'#*'$;,<βdrslsB>;Zut~Ǻ)a=igWP!>#)샭^(quԨryqTOi6.YRHTwQlvrߵ`n=Ҭfޥsqq07%5X̪qĨuUc2|oq@$q8ܝ=U2moqGnf<r!fB%PDPe[_9 OGk\7#*BU&<? 29M|%lw^._­`N"Dh}ZܜYD뢚M:@$6\eu[Q7nJvv GrvY.I-@BHQ3Hh`14]AwA.:.}Jmv"J/\-\L !$a\fVXI~g-R㳨C19n欄i*^GG{K2MBzT.W7/cMrN|$"Q?*y[6"ƺ{K873C(!%͵LT>{c&A31o#*\pׇizFx]YsCʗC I$;/O;U#Qa"#*`cr6b#*;w;\kg#*/+6kSb1ĬtRrAێ2yu-1Yf>Z!ۺ#*O9[ҷO%qK$xF4^DtwqHrd3'8z|6ș)Qm\z@Aq=0zJ1KR_)Lt;Rz[m88פ6e3ZyOܞ=#JGպ6de6 kL*YY#+bJںr%ҩ#*Yywy=p'F򪋭lg/=a~U0˾{0޵yJ|]%.Iwǯ;#*[qwb3vXqXic8Li%J'ş3s4. r6Dt6cM,ʝ~܈M NPP޲s1fJeީt[h|Tl1')'<u,Gf(kA2ʔ;[xpņj^#*u鮡4)1ow$fV"cR!=b#6.WQ G4U%5!Czt;@lF1`:L”okAa+a 2pu姃<9zp6W:FŮ]p#+A pL@M*7p}@$$gL"kLC*#)@>#+ )@#A#-qlߣqwas3iaHN(FN]F4:0a(@<eghYP s2l750FA5 Dv[sJ|rtmN-3Ðk,2pLWJ%g>vdym98ճI6*fN#+*5/32{\U"*PQb,M ez]Qk]94nzFQWLZKB`c5I4q;Buo||- #i5Qj(dvxwug~E5db#T@/-lZ/yŨPĠ^4n5sNx/-kd:ѝc=Xt牻\!jm.;uW Өk vGMu7%Otn= ru Ǹny_6Y_XL0xNq]3Ͷ  xGT#T8bWo){ɛ.<r r9ԭT+OC*0w-#+X4`#)ϴpR¨ay 9r)tY#ۋp<8UV{@#*7'vs:ᙐ\h0,;}jl<M)vlCaa#偄3f.wK͡C#+\ٗc`դ#*#+:o,1 .#h5.W!N岩T0kGm=L=ԫIj:º HJh&؅3P"9O$Q]i-S6 2I#)2=\IK#)}M@%:#*ފȥ>eAb: `PnQVt#*~#*s!tV\~ g6;4q[1UT4u-)12vC#*MR_gίU#)=idO8Á\v2YͥB,#)_`DamlsԱU%&FbuBD d ShXЂ6c#I#*U@ x"s@ACUkzVnkQZܵl(vR,u5*2ƔTE#+2($F3='3-!#)ɀ۝pR.4$,g9+x܋cm*ζ[EW-TkU^]{27#3t9t~}XeR9c`#+D"5j4{nm֌t1h]2*vlZqU')fMAC*#Hz2Ƴ,"E#+#*aDi6K)a.+M1@%rQSO1#+|'XuHW>lL#7T`&581"v\jV p#+1{zԦZ;YMiMkT7 glԖZtsղ 6Ȇ<`"3@Ŏ ōVP˭+mIK_֧<ݣLd39N+;rDHcBA@ic]cwC1!sR%4ƮeoiY2R .7 R ǷVI'tR٫#*w4狮.D)l|D4< R޺֓cS򣜵.CZFLdq"7*#+* Ѹ0m0m15+Fn-,za[ tC 2CdT]&09EZ*#*hFemF=]qD&Z㙢͈аiSqWU[UO$*7Įif:pw`cL"b[cMUSWE(VUZ1еr3L=&g63r<@5śıqɦ3NUm!C4@]Qj>( QsA_-$u)(h1n#*`9e4<i#+pŅ)L8 4V3<m-aZ2F;gHm&47,J4pɪ$bYj\̬Mng%MfHT06%Tr$!htq`8XqF\X5tc`qRc֮xI/>.vOj,H,F#+Q؆v2I-;V\aݜae2mMڿoe&}vݮHI9;'b NSfƵn1Rnq(0UQR=M5?G5{O{x9Pv5b,Ve&8O'}`o#+2;GU/dJp'ih?wdPlR<gosn7m#*H1"a~rwn͛UdX\<B= r;ƺi![kHポ"̍ge<OE7=`nbIIWjJBHF!6$#*V`@H*#+{Հ7~<Dznm{kׯb5CLci1LcHRPLضKImXlIY4hQMF!BjF$f41HhMJ-$l0لJPcVAUo7/虨Ng:MHd)F]o9΍4Uy\vWy9+Ȓ3#)mz%r6RNum/Rz@#+mCjp%V[1b[p̔0e}0~1xʲ@ks9QBF+ n>.AQtCȢFO>!I!pN]{9WQ FN~ʈ\ѫP #+?3VmD6RmcGZͦƥ#+MFkb3]VZJ}ia, ˿B 3(d"ٛV t`ϊ(TGaEi7?,5XC82V)#f#*UE"#+tF! `#+@qܓ I%jouݺvs^uy1FLX#-(`%$&j#+8E 2lZ( XC>#*F`rgŐn.̴JEIj#L!(HR:n`b9כ1U"tqa!o@D0#!on ##kmA(*kH"p#+KH[ŒDI#U*!ѺL@id)JqKO5^zݑBW#){8~rzdHFBn/~̉:zjپ^H%{҉u:Dx %BɄbce+kS4 B+C",dr1*጗Yxx*7߭*K6׭je%=WTwLMKjDCHĎWػo#`="[S[dʊlA`5%4݊ [ByZr Z}^`z86WҜJ5y<H'YN}G}kɹvSIϜ.b'zFIJS5OflSc/2B I<3Twj7V<@MZ?nj2`#q%A{NqH R,""#+OzGq1/5mPq#{=JaoǵG3x%kٮ{d*hU0\Ctbt^~I;L X=;/#)'?ehO;ʤA/U\yge)^=sDi~{u#9d,٬"]!ntr #d+}»H|YJwa*'*ƦxN#+G x(`Y#)wfAjSuV A/BhI9Qw.֍8e$R5)&n$pc#*҄QF¡0&9팈@p,@2g,3cvAK4"qHd:K'[YlK,䜒aFq*Tp(6<f`P1)5B08olEW<mla\e!uLFa #* X'W$DHawn:v (L弰ɪt2BC #)b [+>7ۗ%/F~yvE2"97@1-$TLNIgl_NVqLɔ|e)1>}~Dr 4vkc#+1e2nмP:"Pԅ$uwZ"4d)/BVb gq'^c5@Ilg ԬǍ^nݭ3yzhⲾ#*3(I#y`pHXls 5vA[魏 :fӽf40mg8pp$3k:{OA9 6q8v5NRd7mX)ʒ(LZ1cvL4f\ydɽ>9LG&F%5!Ҟ8dZ{ vk)m]Hg0H6SDK;K.eψ[m0Zyk.eFM:-_g|X[pBsqWbEX4ƴN2b-M>ʲÂYs-00 SpU1zDS\v'#*tݓJs&itԋ\sH`Za#*YxB˕aVj&dKZ{pwreP_.uaw<mMs\jJݝo{޺g(*r5ih+BFčf8#*\J)9Xw0r:^1;N5!?'*8;_}fഎk(D$N 6)C f`f(i@;MX#+RPM#)Ɠ)S1jnZH+BN\mgY%C]V@#*4ќLk#)"@x;ve9cftR^=k4Q&={Mߊmb5h.m#Is."!ybi|qζ#+lƧwu#)i֯\uK!~~՝#rNzXQL;T_!5qUԾj&7, FJMjfLX|2X[[#51 !#+عb᫁0tLb 6#+#*f \&8>LJdw+m4,(uvZ 0 (K(fcn8>{rko)ި ij!A Q4ZsÖ&&ڦ ,`sD^ەk; MNNpW4W*aۓP&~ןX;Qav3at&nɦG Y.ٮjߒ m1 S{qb: !0dG4l]+Tp:5dAHChEAFH"P5S8:єa0QcN <Slð;*0%DH!.+TКY0#*#*L#)N.jd2#*JC S5&̚,MV6fe`Q:ViE]\Q$xTʮ:!Зz2 *#*lC|ظ#aȪ#+v6f4odADàġddjҥ͕MP6'HD 6N4#+0`Fp$50JuќQ3#)`q:# 2:@#+S&pI`^0giHDByy9FQQ`1M#*c_P3 {!! &U#+(tCy콀:UbTW KvҳS%gy3>F[JPEM܇)|=6MmTchSuHs#3A7b$8l\$ r K5^z䪠Z 2˹DJR*UzRYSAEB;40k4AvM2p#**J;2;˕"pp224P}Q-#*{4GV}&2xvms,:n8֕Ԍ8"s!#*~Pٛ,%5JؒWoxzBBLTE u/UL}*TQ_)τht^C#)ϲ mr>d$uJ%U+'L,P`ƥ#+5Q(NN5Jd*vZ0b#i`+qC&J-jwn6fH $H06B#+AvaHXi#Ԫ iZ4(3 IG\H2?=Íj(b4j%օ#34u*bQ GF;goC wڵ"rNYc/#*XAEF'j#)5#+4V%vHJa^0kbk‚P<Pq)wӹg1g>hQ4eU=jbʯ,_r}C8~&e9/958MHlԟeVBq-(ӅkTMA!zN͸<WjH@ ׅsCaƗG#*5|zgkLY}^H`1-db'(g<#*(ɝ+ZS{b$DZE%kZRmMzXDVBEGEVE!F[L!3p(.(DB&W#);#){rȢH]2"Cg~U!Շv9,a0"HB;t5*c~q+ 8۷*xpop6tthg#*`޻Ž9{[#AZHS0Ѳ ,I6Uko'k.gt;x׮۔ZɍYd,*K4fuHdl,"lf>z8)a 1V=K6-h] s1;&lgN#*76QW"#)16sԅ2gQf08D$PuS @!2)#)&!0PGkT<UZ,CČ?;hW0P'x(e[̅Уo8J-9:T89acJq.Bh=D6g }PQ: PARIJך'|D,5_vV G/ DR)I%d-f&Л(1jm576*dfhwoD#+ o,8dE͟"6ON{( T{NBfA$Ǝ}QsI*74 +ԴA#)7MW!jRCQH6٦S3 @047 G˹,O6PjaWD6f ED&FZе)u㍘#*֐RhIHnb tqVsF?+#+:9\1#+ -2[BU(nC- uވT>1`!}L˸,ťLPTkAV0XXpm/٭c"YXS2`:=/0lQ{䳻#+P]%,B#Ct:L#+0"0!1sUbӸX'̡FA ĒK@9& *'IF/XC^XI{҈PE2t#+!o~;{bG?>9fӇ"vR6f=aE;o=J(#+<Q ې 'wC{G=__snc蚀UIv|5Ҋ7E6sq@]4l:5/0$2c2<]FF&]HlWϼW79瞻RlnyMms\+yv歼ɷVv*M@Peg;%U|Jlx/fzTyyH)P::ß4d#)(Iu=b!Q A")E4jQw#+4Cicmjv&Il? `l!F+a\X]O#aMGmBB.}*J)-fQQfF+X[,m&*mJmc E.#)&s0ȒwMzÀQ>geL|5\~YAdż:{#*XH.ȳ/yq좫!"N3?t &nyBQbp oMw#+@A֡0tZ !BS|WFԖۤ֩"dd騠85ٴU)#*aJ.))VEeCGǞ4!ka5W4&cĴLVu3Wch µ}ƀM%͠9j-&գ{>r61T+`{ ?W@WRC¤iISLJdj)lbMom#WZ(M2ؕ,[(D$#+B<Ы| @*FF&L(MCfҍa`7@}0z/u[imoԍkWmF-ڔ5F6XXR 3:t63kiUJHl}(!X0.)E4ƒQFzQ%遦Ix$ȫmiGl'Yl홑#*Ņ9= EC&WM-i54R4a&4ɭW%Ef̪뛶vm^]Dl*dJlhV@U#*V$Jko"o%i^ˈJil*uo:W3Le]ۢMtٚ HVFZ`&O5UYtXnBQ){Z6g TW9dck?Kh AT.Fo#)9 vp\Mp,ir{Qv~/มpBKy70źs Iڶ)uFἷBˑȦF$ǩCURWtoQ-xz)ad(E#8I⽄#)5:05$3Uw̤ 8;x_j+.IcE<@yr#+R!*F<ֱUfoՁ%hG|6U:Pj ~Vov#*p`LݟlvhKxP4pcoG> 7$5-XI0TR'!4/p֚j ha׻[0Qlc#+]':@Yz\Jm5u܎WC%ۇq<e~e"hK8>Y[9||lpYyL(7|5N<-X 8M[.8#1Yڈp2\{h$읊խPnh6EQFnh*ƅ@[8A ʍ;çA!"1OAsܱ2ȤQht -u1pdl0yo=(1l=E9G"=гx2 os+:ίmm=ڌgTOd  3dL-rCp@CQ~4PUOft#)OfYv35^Usb2#)?$RVwާцRNLL#)p`Ho!Pv#*%ηS+VE 2Z@CbŽQn;$ 33UwU$rφB@;3թ.6kC2tB9DmG}/dpuf鴠m#*'/Ќ{qWbA|3;:hd\:_`6lCt~+ڳ_ˍ=CLC2JCx#+,]d'D$oUrOü#*7KeL1ҷYs#5 {Ro):ZefY#+#+]#*T0eTo[xSs#;^lL++UX9ai̦@Gٹ4#)fkcAlaD!vI^$l,rAC 6bӛ#gw9h8+^{: ~Y&l&6칰Bw$Y#\{b#)c.4B#*LL<2t)zׂ}vo i64k)#+fL}2Bh3^i IqacFU1].`AHIz|KRCd0Uy=#*ax1wfL򹃶 ۳]8,gl+P=X#+ ♏vf``3(H&p&V P:Pđ@$HBH˴e!Ay(8caƗ0 {M ;Yݲ@X D'-tn`6bt#+DմLjJlkY3[FmDŅS_;ڨ::"fOe#)ȇ#)/ΪZ,Rزs=rˉM@=?##*"Lk]O׬cS9ɬצU1#s\p-ܗumP^ljoEwʠ[%=ǕN7<X?럨N#+3ɚgY,L#+<}<*j8# #*D'GD᡽qIJ5=7mJS{8rW4 Rl8EE]o1h6ܭWWսMT6{@i볰X6*<00HF 22O#+7q q(vع8.P#+Щːǐ7Ub*J9DA]Kƒ\dFn+Sez=(GS/" lyͬPȌ,XmvԍP6kk%2իj&IWѪ$ѨRwP));%ce=g0@fx{}ȝg &l=PH,#+,ZU'?"=d12>6xh?~i/#*<+:qM:uЧGy%ޟ_NV'OA Q $iwDep#+?dY6occ-3{(WQ6#Uf'uĔ6Q*QһxULqs8մD 2ye#p尖7'ו . Ȭ2LMfypx`H$Y@z= jϫ_E=Tg6(g-UF7;mm_y rE8P#*s
#<==
-#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEEivIt5aBoIuNHTzxwSbTGfAUneqoFAl3aW3cACgkQSbTGfAUn\neqruqA//Y9oJ46ZR8W7YB/e45bfrYxGbN7NnkvkwSPNziObYur+n1QpQEOaPTn/U\n5kFtPWHXRJzaG/A9poKn7pl1Xd7Edcu1aalfoEazZbuD37VOxIp9lnrefCAeICqj\nGv0SD96Zac91CbA+b20Q4xnqxKMi3LSI4NPjfFGy62FkSk3MS4p6Rdp0/WAKwwNj\nw7WEjQCNmLb37z+FGSzXg28aljYeteBZEthsVmGJ5QqVwMBwgj2+y5FOTzFfxmqB\nrWgjFYS0l85kgYRZv9yzdNmFs5SScwafwpT8Xmdr49tFn/+0LxXyRxX+rdODgrpV\nY4EOiQz0fd6mMMnaTDXlLSXls3JyVYmbTjeNL/9gcHmnStzJ851CJQfyQg7A+JoC\nc7nz0HbiFyTgB+PUZr1OhGj3A7287o8XQ0tqR3oa7jXIOX0OynrGplMQKr++0jE1\nBgKzjLoE9CTbjkQfICLG+aUy3S1ZyDk/BcO+5+Ytbru+qXuDsIgAdVosMfNSv9jJ\nXvOINsbRMekdejYMZv8fIkn5OEjCFHVhNpobEsCb768bjB3p7alQGECBvjHCm6dy\nXZPzl9cBMWIXcBjPTS+GZj+PIXGcu76pbsx6HBHWf+uJ+4xgOsUCVu//0AV09jvA\n0MjtLWwQ8mdRH6Wt4hsp4HKtSvQrhmljf2OnuYBgaFmcdJkN1zI=\n=C0oT\n-----END PGP SIGNATURE-----\n
+#-----BEGIN PGP SIGNATURE-----\n\niQIzBAABCgAdFiEECzlystnjLqtCPS4PIr4MYv+/pUgFAmOxj14ACgkQIr4MYv+/\npUgg7A//X6agCAo9x3REEDFk4GTbW8nMkH7gD+ixNnMNcDF96pmp1f3Qdkm4DU19\nscpa1IPN5ik9xeU4Xs7+SifSlJrT1h9gj2bCFIPWWVlXjCXbn52mU4lijQ4iyaV7\nwLuD4ebG8UU9QK5gP1U2hERdTbkuOxuhQijTijDpfOuUnR1N5YoZmONszgYvYmAL\nr7Zr+Cuc+0HiEdsgMUabp1LTyY+urkZJoHcOTMe0QoyrTvM/CFgB4V3ppw9006VQ\nV4XdoAXEkNNfdJUE1s+4SrQxWT2AC2n9X7vjxbqNwfglzrujf862JrloOjybffdg\ngMPXwLpfT8TrVHFSs5aQK++8YhgwcgDz4Biqt/aq68iM424XnUYSwkzZTowGM/A7\nhyM7Jh+Mp/Of10pIx6PBbjjhbh3dH4OUtFv48uYLh1V95ICQ538UypMfBOK9mEPL\n8zpyTbQ8U873Ri+awOcpoJ4738gZNHAbEHZ3Ctr+g7eoayNlFMENG8bMrLwOkiUQ\nLZVXzNPtPs/OFe0rYu6okVONs8MLmuArnIy7v21Ti73Dtc4ABP6V4CFfc3AGqO+l\n3SmEb5r31h+iXvc4vdp33tESohxUFosIgMGjD8ZMyVFWy78wgp5I9qSbSPdVhl60\nF8WGX/ydvcf6D8NDZqjrsRyFtxKn6xLRQgUI+6DKsNLwRzJBVas=\n=mYk7\n-----END PGP SIGNATURE-----\n