gnatcoll-core-21.0.0/0000755000175000017500000000000013743647711014202 5ustar nicolasnicolasgnatcoll-core-21.0.0/.travis-install.sh0000644000175000017500000000246513661715457017601 0ustar nicolasnicolasset -e set -x mkdir -p $TOOLS_DIR || true cd $TOOLS_DIR # If not already present, download the GNAT Community installer and the helper # scripts to use it headless. if ! [ -f $GNAT_INSTALLER ] then if [ "$TRAVIS_OS_NAME" = "osx" ] then wget $GNAT_OSX_INSTALLER_URL -O $GNAT_INSTALLER else wget $GNAT_LINUX_INSTALLER_URL -O $GNAT_INSTALLER fi fi if ! [ -d gnat_community_install_script ] then git clone https://github.com/AdaCore/gnat_community_install_script.git; else (cd gnat_community_install_script && git pull) fi # If not already installed, install GNAT Community. The script does not work if # the installation directory already exists, so remove it first. if ! [ -f "$INSTALL_DIR/bin/gprbuild" ] then rm -rf "$INSTALL_DIR" sh gnat_community_install_script/install_package.sh \ "$GNAT_INSTALLER" "$INSTALL_DIR" fi # Log info about the toolchain we use - which gprbuild && gprbuild --version # Checkout gprbuild sources (or update them), and then build and install # Libgpr. if ! [ -d libgpr-src ] then git clone https://github.com/AdaCore/gprbuild libgpr-src; else (cd libgpr-src && git pull) fi ( cd libgpr-src make libgpr.build make libgpr.install ) # Install e3-testsuite to run GNATCOLL's testsuite pip install git+https://github.com/AdaCore/e3-testsuite.git gnatcoll-core-21.0.0/README.md0000644000175000017500000000501113661715457015460 0ustar nicolasnicolasThe GNAT Components Collection (GNATcoll) - Core packages ========================================================= This is the core module of the GNAT Components Collection. Please refer to the documentation in the `docs/` directory. Code status =========== Build status with GNAT GPL 2017 Platform | Status ---------|------- Linux | [![Build Status](https://travis-ci.org/AdaCore/gnatcoll-core.svg?branch=master)](https://travis-ci.org/AdaCore/gnatcoll-core) Windows | [![Build status](https://ci.appveyor.com/api/projects/status/31a7dh523xto7b9f/branch/master?svg=true)](https://ci.appveyor.com/project/github-integration-adacore/gnatcoll-core/branch/master) Dependencies ------------ GNATCOLL only depends on a recent GNAT compiler. Building it also requires GPRbuild. Configuring the build process ----------------------------- The following variables can be used to configure the build process: General: * `prefix`: location of the installation, the default is the running GNAT installation root. * `BUILD`: control the build options: `PROD` (default) or `DEBUG` * `PROCESSORS`: parallel compilation (default is 0, which uses all available cores) * `TARGET`: for cross-compilation, auto-detected for native platforms * `SOURCE_DIR`: for out-of-tree build * `INTEGRATED`: if `yes` (default is `no`), consider that `prefix` is where the toolchain is installed and install GNATcoll in a target-dependent subdirectory. This makes it possible to install GNATcoll multiple times for the various compilers in the same prefix. Enable this only for cross compilers. Module-specific: * `GNATCOLL_MMAP`: whether MMAP is supported (yes/no) default is "yes"; has no effect on Windows * `GNATCOLL_MADVISE`: whether MADVISE is supported (yes/no) default is "yes"; has no effect on Windows To use the default options: ```sh $ make setup ``` For example, to setup GNATcoll to install a debug version in `/opt/libgnatcoll`: ```sh $ make prefix=/opt/libgnatcoll BUILD=DEBUG install ``` Building -------- Building all versions of the GNATCOLL Core Packages (static, relocatable and static-pic) is as easy as running `make` in the top directory. Then, to install it: ```sh $ make install ``` Note that underneath, this Makefile uses a GPR project file: `gnatcoll.gpr`. You can build GNATCOLL using it with GPRbuild, but make sure to use the same command-line options. Bug reports ----------- Please send questions and bug reports to report@adacore.com following the same procedures used to submit reports with the GNAT toolset itself. gnatcoll-core-21.0.0/Makefile0000644000175000017500000001400513661715457015644 0ustar nicolasnicolas############################################################################## ## ## ## GNATCOLL LIBRARY ## ## ## ## Copyright (C) 2017, AdaCore. ## ## ## ## This library is free software; you can redistribute it and/or modify it ## ## under terms of the GNU General Public License as published by the Free ## ## Software Foundation; either version 3, or (at your option) any later ## ## version. This library is distributed in the hope that it will be useful, ## ## but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN# ## ## TABILITY or FITNESS FOR A PARTICULAR PURPOSE. ## ## ## ## As a special exception under Section 7 of GPL version 3, you are granted ## ## additional permissions described in the GCC Runtime Library Exception, ## ## version 3.1, as published by the Free Software Foundation. ## ## ## ## You should have received a copy of the GNU General Public License and ## ## a copy of the GCC Runtime Library Exception along with this program; ## ## see the files COPYING3 and COPYING.RUNTIME respectively. If not, see ## ## . ## ## ## ############################################################################## # Makefile targets # ---------------- # # Setup: make [VAR=VALUE] setup (see below) # Build: make # Install: make install # Variables which can be set: # # General: # # prefix : root install directory # ENABLE_SHARED : yes / no (or empty) # BUILD : DEBUG PROD # PROCESSORS : nb parallel compilations (0 to use all cores) # TARGET : target triplet for cross-compilation # INTEGRATED : installs the project as part of the compiler installation; # this adds NORMALIZED_TARGET subdir to prefix # # Project specific: # # GNATCOLL_MMAP : whether MMAP is supported (yes/no) # default is "yes"; has no effect on Windows # GNATCOLL_MADVISE : whether MADVISE is supported (yes/no) # default is "yes"; has no effect on Windows # helper programs CAT := cat ECHO := echo WHICH := which # check for out-of-tree build SOURCE_DIR := $(dir $(MAKEFILE_LIST)) ifeq ($(SOURCE_DIR),./) RBD= GNATCOLL_GPR=gnatcoll.gpr MAKEPREFIX= else RBD=--relocate-build-tree GNATCOLL_GPR=$(SOURCE_DIR)/gnatcoll.gpr MAKEPREFIX=$(SOURCE_DIR)/ endif TARGET := $(shell gcc -dumpmachine) NORMALIZED_TARGET := $(subst normalized_target:,,$(wordlist 6,6,$(shell gprconfig --config=ada --target=$(TARGET) --mi-show-compilers))) ifeq ($(NORMALIZED_TARGET),) $(error No toolchain found for target "$(TARGET)") endif GNATCOLL_OS := $(if $(findstring darwin,$(NORMALIZED_TARGET)),osx,$(if $(findstring windows,$(NORMALIZED_TARGET)),windows,unix)) prefix := $(dir $(shell $(WHICH) gnatls)).. GNATCOLL_VERSION := $(shell $(CAT) $(SOURCE_DIR)/version_information) GNATCOLL_MMAP := yes GNATCOLL_MADVISE := yes BUILD = PROD PROCESSORS = 0 BUILD_DIR = ENABLE_SHARED = yes INTEGRATED = no all: build # Load current setup if any -include makefile.setup GTARGET=--target=$(NORMALIZED_TARGET) ifeq ($(ENABLE_SHARED), yes) LIBRARY_TYPES=static relocatable static-pic else LIBRARY_TYPES=static endif ifeq ($(INTEGRATED), yes) integrated_install=/$(NORMALIZED_TARGET) endif GPR_VARS=-XGNATCOLL_MMAP=$(GNATCOLL_MMAP) \ -XGNATCOLL_MADVISE=$(GNATCOLL_MADVISE) \ -XGNATCOLL_VERSION=$(GNATCOLL_VERSION) \ -XGNATCOLL_OS=$(GNATCOLL_OS) \ -XBUILD=$(BUILD) # Used to pass extra options to GPRBUILD, like -d for instance GPRBUILD_OPTIONS= BUILDER=gprbuild -p -m $(GTARGET) $(RBD) -j$(PROCESSORS) $(GPR_VARS) \ $(GPRBUILD_OPTIONS) INSTALLER=gprinstall -p -f $(GTARGET) $(GPR_VARS) \ $(RBD) --sources-subdir=include/gnatcoll --prefix=$(prefix)$(integrated_install) CLEANER=gprclean -q $(RBD) $(GTARGET) UNINSTALLER=$(INSTALLER) -p -f --install-name=gnatcoll --uninstall ######### # build # ######### build: $(LIBRARY_TYPES:%=build-%) build-%: $(BUILDER) -XLIBRARY_TYPE=$* -XXMLADA_BUILD=$* -XGPR_BUILD=$* \ $(GPR_VARS) $(GNATCOLL_GPR) ########### # Install # ########### uninstall: ifneq (,$(wildcard $(prefix)$(integrated_install)/share/gpr/manifests/gnatcoll)) $(UNINSTALLER) $(GNATCOLL_GPR) endif install: uninstall $(LIBRARY_TYPES:%=install-%) install-%: $(INSTALLER) -XLIBRARY_TYPE=$* -XXMLADA_BUILD=$* -XGPR_BUILD=$* \ --build-name=$* $(GPR_VARS) \ --build-var=LIBRARY_TYPE --build-var=GNATCOLL_BUILD \ --build-var=GNATCOLL_CORE_BUILD $(GNATCOLL_GPR) ########### # Cleanup # ########### clean: $(LIBRARY_TYPES:%=clean-%) clean-%: -$(CLEANER) -XLIBRARY_TYPE=$* -XXMLADA_BUILD=$* -XGPR_BUILD=$* \ $(GPR_VARS) $(GNATCOLL_GPR) ######### # setup # ######### .SILENT: setup setup: $(ECHO) "prefix=$(prefix)" > makefile.setup $(ECHO) "ENABLE_SHARED=$(ENABLE_SHARED)" >> makefile.setup $(ECHO) "INTEGRATED=$(INTEGRATED)" >> makefile.setup $(ECHO) "BUILD=$(BUILD)" >> makefile.setup $(ECHO) "PROCESSORS=$(PROCESSORS)" >> makefile.setup $(ECHO) "TARGET=$(TARGET)" >> makefile.setup $(ECHO) "SOURCE_DIR=$(SOURCE_DIR)" >> makefile.setup $(ECHO) "GNATCOLL_OS=$(GNATCOLL_OS)" >> makefile.setup $(ECHO) "GNATCOLL_VERSION=$(GNATCOLL_VERSION)" >> makefile.setup $(ECHO) "GNATCOLL_MMAP=$(GNATCOLL_MMAP)" >> makefile.setup $(ECHO) "GNATCOLL_MADVISE=$(GNATCOLL_MADVISE)" >> makefile.setup # Let gprbuild handle parallelisation. In general, we don't support parallel # runs in this Makefile, as concurrent gprinstall processes may crash. .NOTPARALLEL: gnatcoll-core-21.0.0/testsuite/0000755000175000017500000000000013743647711016233 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/run-tests0000755000175000017500000001521713743647711020133 0ustar nicolasnicolas#!/usr/bin/env python from drivers import make_gnatcoll, TESTSUITE_ROOT_DIR from drivers.basic import BasicTestDriver from drivers.json_validation import JSONValidationDriver from drivers.data_validation import DataValidationDriver from drivers.gnatcov import list_to_file, produce_report from e3.testsuite import Testsuite from e3.fs import mkdir, ls, find, rm from e3.os.process import Run import re import os import logging class MyTestsuite(Testsuite): enable_cross_support = True tests_subdir = 'tests' test_driver_map = { 'json_validation': JSONValidationDriver, 'data_validation': DataValidationDriver, 'default': BasicTestDriver} def add_options(self, parser): parser.add_argument( '--gcov', help="compute code coverage of GNATcoll with gcov", default=False, action="store_true") parser.add_argument( '--gnatcov', help="compute code coverage of GNATcoll with GNATcoverage", default=False, action="store_true") parser.add_argument( '--valgrind', help="check memory usage with Valgrind (memcheck tool)", action="store_true") parser.add_argument( '--recompile', help="recompile debug version of gnatcoll for testing", default=False, action="store_true") def set_up(self): self.env.gcov = self.main.args.gcov self.env.gnatcov = self.main.args.gnatcov self.env.valgrind = self.main.args.valgrind self.env.enable_cleanup = self.main.args.enable_cleanup # Reject incompatible options incompatible = [name for name in ('gcov', 'gnatcov', 'valgrind') if getattr(self.env, name)] if len(incompatible) > 1: raise RuntimeError('The following options are incompatible: {}' .format(' '.join(incompatible))) # If doing a gcov or GNATcoverage-based testsuite run, rebuild GNATcoll # accordingly and update directories. if self.env.gcov or self.env.gnatcov: if self.env.gcov: subdir = 'gcov' else: subdir = 'gnatcov' work_dir = os.path.join(TESTSUITE_ROOT_DIR, subdir) gpr_dir, src_dir, obj_dir = make_gnatcoll( work_dir, gcov=self.env.gcov, gnatcov=self.env.gnatcov) self.env.gnatcoll_gpr_dir = gpr_dir self.env.gnatcoll_src_dir = src_dir self.env.gnatcoll_obj_dir = obj_dir if self.env.gnatcov: # Prepare a directory to host checkpoint files. Make sure it's # empty so that we don't re-use coverage data from previous # runs. self.env.checkpoints_dir = os.path.join( TESTSUITE_ROOT_DIR, 'gnatcov', 'checkpoints') rm(self.env.checkpoints_dir, recursive=True) mkdir(self.env.checkpoints_dir) # Gather the list of ALI files self.env.ali_files_list = os.path.join( self.env.gnatcoll_obj_dir, 'ali_files.txt') list_to_file(find(self.env.gnatcoll_obj_dir, '*.ali'), self.env.ali_files_list) else: self.env.gnatcoll_gpr_dir = None if self.main.args.recompile: work_dir = os.path.join(TESTSUITE_ROOT_DIR, 'debug') gpr_dir, _, _ = make_gnatcoll(work_dir, debug=True) self.env.gnatcoll_debug_gpr_dir = gpr_dir if self.env.gnatcoll_gpr_dir is None: self.env.gnatcoll_gpr_dir = gpr_dir else: self.env.gnatcoll_debug_gpr_dir = None def tear_down(self): wd = TESTSUITE_ROOT_DIR # If requested, produce coverage reports if self.env.gcov: # We need to call gcov on gcda present both in gnatcoll itself and # tests (for generics coverage). gcda_files = \ find(os.path.join(self.env.gnatcoll_obj_dir), '*.gcda') + \ find(os.path.join(self.env.working_dir), '*.gcda') mkdir(os.path.join(wd, 'gcov', 'results')) gcr = os.path.join(wd, 'gcov', 'results') Run(['gcov'] + gcda_files, cwd=os.path.join(wd, 'gcov', 'results')) total_sources = 0 total_covered = 0 for source_file in ls( os.path.join(self.env.gnatcoll_src_dir, '*')): base_file = os.path.basename(source_file) if not os.path.isfile(os.path.join(gcr, base_file + '.gcov')): total = 1 covered = 0 with open(source_file) as fd: total = len([line for line in fd if line.strip() and not re.match(r' *--', line)]) else: with open(os.path.join(gcr, base_file + '.gcov')) as fd: total = 0 covered = 0 for line in fd: if re.match(r' *-:', line): pass elif re.match(r' *[#=]{5}:', line): total += 1 else: total += 1 covered += 1 total_sources += total total_covered += covered logging.info('%6.2f %% %8d/%-8d %s', float(covered) * 100.0 / float(total), covered, total, os.path.basename(source_file)) logging.info('%6.2f %% %8d/%-8d %s', float(total_covered) * 100.0 / float(total_sources), total_covered, total_sources, 'TOTAL') elif self.env.gnatcov: checkpoint_list = os.path.join(wd, 'gnatcov', 'checkpoints', 'checkpoint_list.txt') list_to_file(find(self.env.checkpoints_dir, '*.ckpt'), checkpoint_list) produce_report(os.path.join(wd, 'gnatcov', 'results'), checkpoint_list, self.env.gnatcoll_src_dir) super(MyTestsuite, self).tear_down() @property def default_driver(self): return 'default' if __name__ == '__main__': suite = MyTestsuite(os.path.dirname(__file__)) suite.testsuite_main() gnatcoll-core-21.0.0/testsuite/README.md0000644000175000017500000001173113661715457017517 0ustar nicolasnicolasRunning GNATcoll Testsuite ========================== `The testsuite is currently under construction !` Getting Started ------------------- To run it you need to have **Python** installed along with the package **e3-testsuite**. To install e3-testsuite: ```sh pip install git+https://github.com/AdaCore/e3-testsuite.git ``` Then do ```sh ./run-tests ``` By default the test suite will be run with the **GNATcoll** library found in the environment. A summary of the results will be displayed once the testsuite ends. Detailed results and logs can be found for each test in the `out/new` subdirectory. In this directory a **YaML** file will be created for each test. In order to have coverage information with **gcov**, just add `--gcov`. It will recompiles **GNATcoll** with coverage information and a summary of the coverage information will be displayed at the end of the test suite run. Full coverage information can be found in `gcov/results` subdirectory. Running the testsuite -------------------------- ### Partial Runs In some contexts, it might be useful to run only subsets of the testsuite. In order to do so you can use two different workflows. #### E3-test Call ``e3-test`` from any subdirectory of the testsuite will run only the tests contained in that directory. This workflow is useful when working for example in a single tests. By default you the testsuite will be run with default parameters, but you can adjust the default parameters by editing the **YaML** file called ``e3-test.yaml`` located in the root directory of the testsuite. The ``default_args`` can be used to add default parameters such as ``--gcov`` for example. #### Run-tests Call ``./run-tests`` with a list of test directories ### Which GNATcoll library is used ? If the testsuite is launched without any argument, then the **GNATcoll** from the user environment will be picked. If you add ``--gcov`` switch then **GNATcoll** will be recompiled using sources from your current checkout. This **GNATcoll** will be used for all tests except the one with the ``no-coverage`` marker in their description (see format of ``test.yaml`` section). Adding ``--recompile`` will recompile a **GNATcoll** library in production mode to be used by the testsuite. If both ``--gcov`` and ``--recompile`` are used then the production mode version of the library will be used only for tests with the ``no-coverage`` marker (might be useful for tests doing some performance measurement). ### Reference Run ``run-tests --help`` to get the full list of options Writing tests ------------- Testcases are found in the ``tests`` subdirectory. A testcase is a directory containing the file called `test.yaml`. A ``test.yaml`` looks like: ```yaml # Mandatory description: My test description # Specify the test driver to be used. If not specified the default driver # called 'default' is used driver: driver_name ``` Some additional information which is driver specific might be present. To get a list of available drivers look for ``DRIVERS`` dictionary in run-tests script. From there you will be able to locate the **Python** class that implement that driver along with its docstring. ### The default driver In this readme we will only document the default driver. The default driver follow the following workflow: 1. Check if the test should be skipped. 2. If not build the test 3. Run the test and check that the output contains some expected patterns A minimal test should contains only one **Ada** unit called ``test.adb`` that contains a function ``Test``. The skeleton of that function should look like ```ada -- Following unit is provided by the testsuite in support subdirectory with Test_Assert; function Test return Integer is begin Test_Assert.Assert (True, "my test is ok :-)"); return Test_Assert.Report; end Test; ``` You can override the default project by creating a file called ``test.gpr`` in the test directory. You can also changed the name of the executable that is executed by setting the ``test_exe`` key in ``test.yaml`` (default value is ``obj/test``). For some specific case for which you never want to enable coverage instrumentation, just add ``no-coverage: True`` to ``test.yaml``. If you need some data files while running your tests, you need to specify them using the ``data`` key. For example: ```yaml description: Loading projects data: - "*.gpr" ``` Will copy all local ``.gpr`` files to the working directory before executing the test. Test can also be skipped based on a set of given conditions. For example: ```yaml description: A test skip: - ['XFAIL', 'env.build.os.name == "windows"'] ``` The skip entry is a list of tuple of the form (status, condition). If the condition (a **Python** expression) is True then test is skipped and test status set to ``status``. Note that currently only the following symbols are available in the conditions: ``env`` (a BaseEnv object), ``test_env`` (the test.yaml file as a **Python** dict) and the function ``disk_space`` (return the available disk space in the working directory). gnatcoll-core-21.0.0/testsuite/e3-test.yaml0000644000175000017500000000004113661715457020400 0ustar nicolasnicolasmain: run-tests default_args: [] gnatcoll-core-21.0.0/testsuite/drivers/0000755000175000017500000000000013743647711017711 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/drivers/valgrind.py0000644000175000017500000000100713743647711022067 0ustar nicolasnicolasfrom drivers import bin_check_call def check_call_valgrind(driver, cmd, test_name=None, result=None, **kwargs): """ Wrapper for `e3.testsuite.process` that runs the process under Valgrind if this is a Valgrind-checked testsuite run. The process exit status will be 2 if Valgrind finds memory issues. """ if driver.env.valgrind: cmd = ['valgrind', '-q', '--error-exitcode=2', '--leak-check=full'] + cmd return bin_check_call(driver, cmd, test_name, result, **kwargs) gnatcoll-core-21.0.0/testsuite/drivers/basic.py0000644000175000017500000000413113743647711021343 0ustar nicolasnicolasimport os from e3.fs import cp from e3.os.fs import df from e3.testsuite.driver.classic import ClassicTestDriver from e3.testsuite.control import YAMLTestControlCreator from drivers import gprbuild, run_test_program class BasicTestDriver(ClassicTestDriver): """Default GNATcoll testsuite driver. In order to declare a test: 1- Create a directory with a test.yaml inside 2- Add test sources in that directory 3- Add a main called test.adb that use support/test_assert.ads package. 4- Do not put test.gpr there, it breaks the test, if you need a project file for testing, name it something else. 5- If you need additional files for you test, list them in test.yaml: data: - "your_file1" - "your_file2" """ # We want to copy only specific files (files referenced by the "data" key # in test.yaml). copy_test_directory = False @property def test_control_creator(self): return YAMLTestControlCreator({ 'env': self.env, 'test_env': self.test_env, 'disk_space': lambda: df(self.env.working_dir) }) def run(self): # Build the test program if self.test_env.get('no-coverage'): gpr_project_path = self.env.gnatcoll_debug_gpr_dir else: gpr_project_path = self.env.gnatcoll_gpr_dir gprbuild(self, gcov=self.env.gcov, gpr_project_path=gpr_project_path) # Copy the requested data files for data in self.test_env.get('data', []): cp(os.path.join(self.test_env['test_dir'], data), self.test_env['working_dir'], recursive=True) # Run the test program test_exe = self.test_env.get('test_exe', 'obj/test') process = run_test_program( self, [os.path.join(self.test_env['working_dir'], test_exe)], timeout=self.default_process_timeout) self.output += process.out.decode('utf-8') def compute_failures(self): return (['Success marker not found'] if '<=== TEST PASSED ===>' not in self.result.log.log else []) gnatcoll-core-21.0.0/testsuite/drivers/__init__.py0000644000175000017500000002046113743647711022025 0ustar nicolasnicolasimport logging import os import subprocess from e3.fs import mkdir from e3.os.process import Run, get_rlimit from e3.testsuite import TestAbort from e3.testsuite.driver import TestDriver from e3.testsuite.driver.classic import ( ClassicTestDriver, ProcessResult, TestAbortWithFailure ) from e3.testsuite.process import check_call from e3.testsuite.result import Log, TestStatus # Root directory of respectively the testsuite and the gnatcoll # repository. TESTSUITE_ROOT_DIR = os.path.dirname( os.path.dirname(os.path.abspath(__file__))) GNATCOLL_ROOT_DIR = os.path.dirname(TESTSUITE_ROOT_DIR) DEFAULT_TIMEOUT = 5 * 60 # 5 minutes def make_gnatcoll(work_dir, debug=False, gcov=False, gnatcov=False): """Build gnatcoll core with or without gcov instrumentation. :param str work_dir: Working directory. GNATcoll is built in `build` subdir and installed in `install` subdir. :param bool debug: Whether to build GNATCOLL in debug mode. Otherwise, use the prod mode. Note that gcov and gnatcov modes automatically enable debug mode. :param bool gcov: If true, build GNATCOLL with gcov instrumentation in debgu mode. :param bool gnatcov: If True, build GNATCOLL with the compile options that GNATcoverage require in debug mode. :return: A triplet (project path, source path, object path). :rtype: (str, str, str) :raise AssertError: In case compilation of installation fails. """ assert not (gcov and gnatcov) if gcov: tag = ' (gcov)' elif gnatcov: tag = ' (gnatcov)' else: tag = '' logging.info('Compiling gnatcoll{}'.format(tag)) # Create build tree structure build_dir = os.path.join(work_dir, 'build') install_dir = os.path.join(work_dir, 'install') mkdir(build_dir) mkdir(install_dir) # Compute make invocation make_gnatcoll_cmd = [ 'make', '-f', os.path.join(GNATCOLL_ROOT_DIR, 'Makefile'), 'ENABLE_SHARED=no', 'BUILD={}'.format('DEBUG' if debug or gcov or gnatcov else 'PROD')] if gcov: make_gnatcoll_cmd += [ 'GPRBUILD_OPTIONS=-cargs -fprofile-arcs -ftest-coverage' ' -cargs:Ada -gnatwn' ' -gargs'] elif gnatcov: make_gnatcoll_cmd += [ 'GPRBUILD_OPTIONS=-cargs -fdump-scos -fpreserve-control-flow' ' -gargs'] # Build & Install p = Run(make_gnatcoll_cmd, cwd=build_dir, timeout=DEFAULT_TIMEOUT) assert p.status == 0, "gnatcoll build failed:\n%s" % p.out p = Run(make_gnatcoll_cmd + ['prefix=%s' % install_dir, 'install'], cwd=build_dir, timeout=DEFAULT_TIMEOUT) assert p.status == 0, "gnatcoll installation failed:\n%s" % p.out return (os.path.join(install_dir, 'share', 'gpr'), os.path.join(install_dir, 'include', 'gnatcoll'), os.path.join(build_dir, 'obj', 'gnatcoll', 'static')) def gprbuild(driver, project_file=None, cwd=None, gcov=False, scenario=None, gpr_project_path=None, timeout=DEFAULT_TIMEOUT, **kwargs): """Launch gprbuild. :param project_file: project file to compile. If None, we looks first for a test.gpr in the test dir and otherwise fallback on the common test.gpr project of the support subdir of the testsuite. :type project_file: str :param cwd: directory in which to run gprbuild. If None the gprbuild build is run in the default working dir for the test. :type cwd: str | None :param gcov: if True link with gcov libraries :type gcov: bool :param scenario: scenario variable values :type scenario: dict :param gpr_project_path: if not None prepent this value to GPR_PROJECT_PATH :type gpr_project_path: None | str :param kwargs: additional keyword arguements are passed to e3.testsuite.process.check_call function :return: True on successful completion :rtype: bool """ if scenario is None: scenario = {} if project_file is None: project_file = os.path.join(driver.test_env['test_dir'], 'test.gpr') if not os.path.isfile(project_file): project_file = os.path.join(TESTSUITE_ROOT_DIR, 'support', 'test.gpr') scenario['TEST_SOURCES'] = driver.test_env['test_dir'] if cwd is None: cwd = driver.test_env['working_dir'] mkdir(cwd) gprbuild_cmd = [ 'gprbuild', '--relocate-build-tree', '-p', '-P', project_file] for k, v in scenario.items(): gprbuild_cmd.append('-X%s=%s' % (k, v)) if gcov: gprbuild_cmd += ['-largs', '-lgcov', '-cargs', '-fprofile-arcs', '-ftest-coverage', '-g'] elif driver.env.gnatcov: # TODO: GNATcoverage relies on debug info to do its magic. It needs # consistent paths to source files in the debug info, so do not build # tests with debug info, as they will reference installed sources # (while GNATCOLL objects reference original sources). gprbuild_cmd += ['-g0'] # Adjust process environment env = kwargs.pop('env', None) ignore_environ = kwargs.pop('ignore_environ', True) if env is None: env = {} ignore_environ = False if gpr_project_path: new_gpr_path = gpr_project_path if 'GPR_PROJECT_PATH' in os.environ: new_gpr_path += os.path.pathsep + os.environ['GPR_PROJECT_PATH'] env['GPR_PROJECT_PATH'] = new_gpr_path check_call( driver, gprbuild_cmd, cwd=cwd, env=env, ignore_environ=ignore_environ, timeout=timeout, **kwargs) # If we get there it means the build succeeded. return True def bin_check_call(driver, cmd, test_name=None, result=None, timeout=None, env=None, cwd=None): if cwd is None and "working_dir" in driver.test_env: cwd = driver.test_env["working_dir"] if result is None: result = driver.result if test_name is None: test_name = driver.test_name if timeout is not None: cmd = [get_rlimit(), str(timeout)] + cmd # Use directly subprocess instead of e3.os.process.Run, since the latter # does not handle binary outputs. subp = subprocess.Popen( cmd, cwd=cwd, env=env, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) stdout, _ = subp.communicate() process = ProcessResult(subp.returncode, stdout) result.processes.append( { "output": Log(stdout), "status": process.status, "cmd": cmd, "timeout": timeout, "env": env, "cwd": cwd, } ) # Append the status code and process output to the log to ease post-mortem # investigation. result.log += "Status code: {}\n".format(process.status) result.log += "Output:\n" try: stdout = stdout.decode('utf-8') except UnicodeDecodeError: stdout = str(stdout) result.log += stdout if process.status != 0: if isinstance(driver, ClassicTestDriver): raise TestAbortWithFailure('command call fails') else: result.set_status(TestStatus.FAIL, "command call fails") driver.push_result(result) raise TestAbort return process def run_test_program(driver, cmd, test_name=None, result=None, **kwargs): """ Run a test program. This dispatches to running it under Valgrind or "gnatcov run", depending on the testsuite options. """ from drivers.gnatcov import gnatcov_run from drivers.valgrind import check_call_valgrind if driver.env.valgrind: wrapper = check_call_valgrind elif driver.env.gnatcov: wrapper = gnatcov_run else: wrapper = bin_check_call return wrapper(driver, cmd, test_name, result, **kwargs) class GNATcollTestDriver(TestDriver): """Abstract class to share some common facilities.""" DEFAULT_TIMEOUT = 5 * 60 # 5 minutes @property def process_timeout(self): """Timeout (in seconds) for subprocess to launch.""" return self.test_env.get('timeout', self.DEFAULT_TIMEOUT) def run_test_program(self, cmd, test_name=None, result=None, **kwargs): return run_test_program(self, cmd, test_name, result, **kwargs) gnatcoll-core-21.0.0/testsuite/drivers/data_validation.py0000644000175000017500000000546413743647711023417 0ustar nicolasnicolasfrom e3.fs import rm from e3.testsuite.result import TestStatus, TestResult from drivers import GNATcollTestDriver, gprbuild import os class DataValidationDriver(GNATcollTestDriver): """Data validation driver. For each test program call the program with data file defined in data_files key of the test. If the program returns 0 assume that the test passed. """ def add_test(self, dag): self.add_fragment(dag, 'build') tear_down_deps = [] for data_file, description in self.test_env['data_files'].items(): tear_down_deps.append(data_file) self.add_fragment( dag, data_file, fun=self.run_subtest_for(data_file, description), after=['build']) self.add_fragment(dag, 'tear_down', after=tear_down_deps) def run_subtest_for(self, data_file, description): def run_subtest(previous_values, slot): test_name = self.test_name + '.' + data_file result = TestResult(test_name, env=self.test_env) if not previous_values['build']: return TestStatus.FAIL process = self.run_test_program( [os.path.join(self.test_env['working_dir'], self.test_env.get('validator', 'obj/test')), os.path.join(self.test_env['test_dir'], data_file)], test_name=test_name, result=result, timeout=self.process_timeout) return self.validate_result(process, data_file, result) return run_subtest def validate_result(self, process, data_file, result): # Test passes as soon as there is this magic string is its output. Note # that we push a test result only on failure. if b'<=== TEST PASSED ===>' not in process.out: result.set_status(TestStatus.FAIL) self.push_result(result) return True def tear_down(self, previous_values, slot): # If the test program build failed, there is nothing we can do (and the # result for this test is alredy pushed anyway). if not previous_values.get('build'): return False failures = [v for v in previous_values.values() if not isinstance(v, TestStatus) or v != TestStatus.PASS] if failures: self.result.set_status(TestStatus.FAIL, msg="%s subtests failed" % len(failures)) else: self.result.set_status(TestStatus.PASS) self.push_result() if self.env.enable_cleanup: rm(self.test_env['working_dir'], recursive=True) def build(self, previous_values, slot): return gprbuild(self, gcov=self.env.gcov, gpr_project_path=self.env.gnatcoll_gpr_dir) gnatcoll-core-21.0.0/testsuite/drivers/gnatcov.py0000644000175000017500000000541713743647711021733 0ustar nicolasnicolasimport logging import os from e3.os.process import Run from drivers import bin_check_call COVERAGE_LEVEL = 'stmt+decision' def list_to_file(str_list, filename): """Write a list of strings to a text file. :param list[str] str_list: List of strings to write. :param str filename: Name of the destination file. """ with open(filename, 'w') as f: for item in str_list: f.write(item + '\n') def gnatcov_run(driver, cmd, test_name=None, result=None, **kwargs): """ Wrapper for `bin_check_call` that runs the process under "gnatcov run" and that produces a checkpoint in `driver.env.checkpoints_dir` for the corresponding partial coverage report. """ test_name = test_name or driver.test_env['test_name'] trace_file = os.path.join(driver.test_env['working_dir'], '{}.trace'.format(test_name)) checkpoint_file = os.path.join(driver.env.checkpoints_dir, '{}.ckpt'.format(test_name)) cmd = ['gnatcov', 'run', '-o', trace_file, '-eargs'] + cmd result = bin_check_call(driver, cmd, test_name, result, **kwargs) p = Run(['gnatcov', 'coverage', '--level={}'.format(COVERAGE_LEVEL), '--scos=@{}'.format(driver.env.ali_files_list), '--save-checkpoint={}'.format(checkpoint_file), trace_file]) if p.status: logging.error('converting gnatcov trace file to checkpoint failed:\n' '{}'.format(p.out)) return result def produce_report(output_dir, checkpoint_list, src_dir): """Produce a coverage reports. :param str output_dir: Name of the directory to contain the DHTML coverage report. :param str checkpoint_list: Name of the file that contains the list of checkpoints to use. :param str src_dir: Name of the directory that contains installed sources. """ args = ['gnatcov', 'coverage', '--annotate=dhtml', '--level={}'.format(COVERAGE_LEVEL), '--output-dir={}'.format(output_dir), '--checkpoint=@{}'.format(checkpoint_list), # TODO: GNATcoverage is not be able to find the source file for a # unit that is not used by any test program. This is a problem for # units that are not tested at all. Let it know where to find the # source file to avoid spurious warnings. Note that these units are # reported as uncovered in any case. '--source-search={}'.format(src_dir)] p = Run(args, output=None) if p.status: logging.error('could not produce the coverage report:\n' '{}'.format(p.out)) elif p.out: logging.info('output of "gnatcov coverage" is not empty:\n' '{}'.format(p.out)) gnatcoll-core-21.0.0/testsuite/drivers/json_validation.py0000644000175000017500000000172613743647711023454 0ustar nicolasnicolasfrom e3.testsuite.result import TestStatus from drivers.data_validation import DataValidationDriver import os import json import logging class JSONValidationDriver(DataValidationDriver): def validate_result(self, process, data_file, result): # Read data file with open(os.path.join(self.test_env['test_dir'], data_file), encoding='utf-8') as fd: expected = json.load(fd) got = json.loads(process.out.decode('utf-8')) if got != expected: # Escape non-ASCII codepoints. This is necessary to avoid errors # when logging wide characters on ancient encodings, such as CP1252 # on Windows. got = got.encode('unicode_escape').decode('ascii') expected = expected.encode('unicode_escape').decode('ascii') logging.debug('%s\n<=>\n%s', got, expected) result.set_status(TestStatus.FAIL) result.push_result() return True gnatcoll-core-21.0.0/testsuite/support/0000755000175000017500000000000013661715457017751 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/support/test_remote.ads0000644000175000017500000000743513661715457023005 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- The package provides remote support using only local resources. This -- allows to test remote functionalities without the need for a remote host. with GNAT.Expect; with GNAT.Strings; with GNATCOLL.Remote; with GNATCOLL.VFS_Types; with GNATCOLL.Remote.DB; package Test_Remote is package GR renames GNATCOLL.Remote; package GRDB renames GNATCOLL.Remote.DB; package GVT renames GNATCOLL.VFS_Types; -- Declare local transport protocol (basically spawn /bin/bash) type Local_Transport is new GR.Server_Record with null record; function Nickname (Server : Local_Transport) return String; function Shell_FS (Server : Local_Transport) return GVT.FS_Type; procedure Execute_Remotely (Server : access Local_Transport; Args : GNAT.Strings.String_List; Status : out Boolean; Execution_Directory : GVT.FS_String := ""); procedure Execute_Remotely (Server : access Local_Transport; Args : GNAT.Strings.String_List; Result : out GNAT.Strings.String_Access; Status : out Boolean; Execution_Directory : GVT.FS_String := ""); procedure Spawn_Remotely (Server : access Local_Transport; Descriptor : out GNAT.Expect.Process_Descriptor_Access; Args : GNAT.Strings.String_List); -- Declare local remote database which holds only one host nickname -- called local_test type Local_DB is new GRDB.Remote_Db_Interface with null record; function Is_Configured (Config : Local_DB; Nickname : String) return Boolean; function Get_Server (Config : Local_DB; Nickname : String) return GR.Server_Access; function Nb_Mount_Points (Config : Local_DB; Nickname : String) return Natural; function Get_Mount_Point_Local_Root (Config : Local_DB; Nickname : String; Index : Natural) return GVT.FS_String; function Get_Mount_Point_Host_Root (Config : Local_DB; Nickname : String; Index : Natural) return GVT.FS_String; end Test_Remote; gnatcoll-core-21.0.0/testsuite/support/test.ads0000644000175000017500000000003613661715457021420 0ustar nicolasnicolasfunction Test return Integer; gnatcoll-core-21.0.0/testsuite/support/test_remote.adb0000644000175000017500000001457213661715457022764 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNAT.OS_Lib; with GNAT.Directory_Operations; with GNAT.Expect; with GNAT.Expect.TTY; with Ada.Text_IO; with GNATCOLL.Utils; package body Test_Remote is package Expect renames GNAT.Expect; package OS renames GNAT.OS_Lib; package OS_Path renames GNAT.Directory_Operations; package IO renames Ada.Text_IO; use type GVT.FS_String; -------------- -- Nickname -- -------------- function Nickname (Server : Local_Transport) return String is begin return "local"; end Nickname; -------------- -- Shell_FS -- -------------- function Shell_FS (Server : Local_Transport) return GVT.FS_Type is begin return GVT.FS_Unix; end Shell_FS; ---------------------- -- Execute_Remotely -- ---------------------- procedure Execute_Remotely (Server : access Local_Transport; Args : GNAT.Strings.String_List; Status : out Boolean; Execution_Directory : GVT.FS_String := "") is CWD : constant String := OS_Path.Get_Current_Dir; Script : constant String := GNATCOLL.Utils.Join (" ", Args); Program_Name : constant String := "/bin/bash"; Program_Args : GNAT.Strings.String_List (1 .. 2) := (1 => new String'("-c"), 2 => new String'(Script)); begin if Execution_Directory /= "" then OS_Path.Change_Dir (String (Execution_Directory)); end if; OS.Spawn (Program_Name, Program_Args, Status); if Execution_Directory /= "" then OS_Path.Change_Dir (String (CWD)); end if; end Execute_Remotely; ---------------------- -- Execute_Remotely -- ---------------------- procedure Execute_Remotely (Server : access Local_Transport; Args : GNAT.Strings.String_List; Result : out GNAT.Strings.String_Access; Status : out Boolean; Execution_Directory : GVT.FS_String := "") is CWD : constant String := OS_Path.Get_Current_Dir; Program_Name : constant String := Args (1).all; Program_Args : constant GNAT.Strings.String_List := Args (2 .. Args'Last); Int_Status : aliased Integer; begin if Execution_Directory /= "" then OS_Path.Change_Dir (String (Execution_Directory)); end if; IO.Put_Line (Program_Name); Result := new String' (Expect.Get_Command_Output (Program_Name, Program_Args, "", Int_Status'Access, True)); if Int_Status = 0 then Status := True; else Status := False; end if; if Execution_Directory /= "" then OS_Path.Change_Dir (String (CWD)); end if; end Execute_Remotely; -------------------- -- Spawn_Remotely -- -------------------- procedure Spawn_Remotely (Server : access Local_Transport; Descriptor : out GNAT.Expect.Process_Descriptor_Access; Args : GNAT.Strings.String_List) is Program_Name : constant String := Args (1).all; Program_Args : constant GNAT.Strings.String_List := Args (2 .. Args'Last); begin Descriptor := new GNAT.Expect.TTY.TTY_Process_Descriptor; IO.Put_Line (Program_Name); Expect.Non_Blocking_Spawn (Descriptor.all, Program_Name, Program_Args); end Spawn_Remotely; ------------------- -- Is_Configured -- ------------------- function Is_Configured (Config : Local_DB; Nickname : String) return Boolean is begin if Nickname = "local_test" then return True; else return False; end if; end Is_Configured; ---------------- -- Get_Server -- ---------------- function Get_Server (Config : Local_DB; Nickname : String) return GR.Server_Access is begin return new Local_Transport; end Get_Server; --------------------- -- Nb_Mount_Points -- --------------------- function Nb_Mount_Points (Config : Local_DB; Nickname : String) return Natural is begin return 1; end Nb_Mount_Points; -------------------------------- -- Get_Mount_Point_Local_Root -- -------------------------------- function Get_Mount_Point_Local_Root (Config : Local_DB; Nickname : String; Index : Natural) return GVT.FS_String is begin return "/tmp/local_vfs"; end Get_Mount_Point_Local_Root; ------------------------------- -- Get_Mount_Point_Host_Root -- ------------------------------- function Get_Mount_Point_Host_Root (Config : Local_DB; Nickname : String; Index : Natural) return GVT.FS_String is begin return "/tmp/remote_vfs"; end Get_Mount_Point_Host_Root; end Test_Remote; gnatcoll-core-21.0.0/testsuite/support/test.gpr0000644000175000017500000000127713661715457021451 0ustar nicolasnicolas-- Default project use for tests -- -- The scenario variable TEST_SOURCES is automatically set by the -- driver to point to the test sources. with "gnatcoll"; project Test is Test_Sources := External("TEST_SOURCES"); for Source_Dirs use (".", Test_Sources); for Main use ("test.adb"); for Languages use ("Ada", "C"); for Object_Dir use "obj"; package Compiler is -- Building test programs in debug mode makes it easier to work with -- tests. for Switches ("Ada") use ("-g", "-O1", "-gnata", "-gnatyg", "-gnateE", "-gnatwaCJe", "-fstack-check", "-gnatw.P"); for Switches ("C") use ("-g", "-Wunreachable-code"); end Compiler; end Test; gnatcoll-core-21.0.0/testsuite/support/test_assert.ads0000644000175000017500000001075413661715457023011 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- Helper package to implement tests that comply with the expectations -- of the default test driver. with Ada.Strings.UTF_Encoding; with Ada.Calendar; use Ada.Calendar; with GNAT.Source_Info; with GNATCOLL.VFS; package Test_Assert is package SI renames GNAT.Source_Info; package VFS renames GNATCOLL.VFS; package UTF8 renames Ada.Strings.UTF_Encoding; Final_Status : Natural := 0; procedure Assert (Success : Boolean; Msg : String := ""; Location : String := SI.Source_Location); -- If Success is True then test case is considered PASSED, otherwise -- the test status is FAILED and Final_Status set to 1. procedure Assert (Left, Right : String; Msg : String := ""; Location : String := SI.Source_Location); -- If Left = Right then test case is considered PASSED, otherwise -- the test status is FAILED and Final_Status set to 1. procedure Assert (Left : Wide_String; Right : UTF8.UTF_8_String; Msg : String := ""; Location : String := SI.Source_Location); -- If Left = Right then test case is considered PASSED, otherwise -- the test status is FAILED and Final_Status set to 1. procedure Assert_Inferior (Left : Time; Right : Time; Msg : String := ""; Location : String := SI.Source_Location); -- If Left <= Right then test case is considred PASSED, otherwise -- the test status is FAILED and Final_Status set to 1. procedure Assert (Left : Integer; Right : Integer; Msg : String := ""; Location : String := SI.Source_Location); -- If Left = Right then test case is considered PASSED, otherwise -- the test status is FAILED and Final_Status set to 1. procedure Assert (Left, Right : VFS.Virtual_File; Msg : String := ""; Location : String := SI.Source_Location); -- If Left = Right then test case is considered PASSED, otherwise -- the test status is FAILED and Final_Status set to 1. Assert_Count : Natural := 0; -- Incremented every time an assert is called. Can be checked against a -- specific value to verify that the expected number of Asserts triggered, -- when their number depends on execution, e.g. if they are called from -- inside callbacks or conditional branches. function Report return Natural; -- Report should be called the following way at the end of a test -- program main function: -- -- return Report; -- -- Testsuite driver will consider a test to PASS if all the -- following conditions are met: -- -- * test program exit with status 0 -- * all assert calls did succeed -- * test program display the message "<=== TEST PASSED ===>" end Test_Assert; gnatcoll-core-21.0.0/testsuite/support/test_assert.adb0000644000175000017500000001503313661715457022763 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Text_IO; with Ada.Strings.UTF_Encoding.Wide_Strings; with Ada.Calendar.Conversions; use Ada.Calendar.Conversions; with Ada.Calendar.Formatting; use Ada.Calendar.Formatting; package body Test_Assert is package IO renames Ada.Text_IO; package UTF renames Ada.Strings.UTF_Encoding.Wide_Strings; procedure Put_Indented (Indent_Columns : Natural; Lines : String); -- Put Lines on the standard output. This also indents all but the first -- line with Indent_Column spaces. ------------ -- Assert -- ------------ procedure Assert (Success : Boolean; Msg : String := ""; Location : String := SI.Source_Location) is begin -- Start with an indicator about test status so that it is easy to -- quickly spot failing tests. if Success then IO.Put ("OK "); else IO.Put ("FAIL"); Final_Status := 1; end if; -- Then tell where the failure happened, and add the given message (if -- any). IO.Put (" " & Location); if Msg'Length > 0 then IO.Put (" " & Msg); end if; IO.New_Line; Assert_Count := Assert_Count + 1; end Assert; ------------------ -- Put_Indented -- ------------------ procedure Put_Indented (Indent_Columns : Natural; Lines : String) is Starting_Line : Boolean := False; begin for C of Lines loop if C = ASCII.LF then Starting_Line := True; elsif Starting_Line then Starting_Line := False; IO.Put ((1 .. Indent_Columns => ' ')); end if; IO.Put (C); end loop; end Put_Indented; ------------ -- Assert -- ------------ procedure Assert (Left, Right : String; Msg : String := ""; Location : String := SI.Source_Location) is Success : constant Boolean := Left = Right; Expected_Prefix : constant String := "expected: "; Got_Prefix : constant String := "got: "; Indent : constant Natural := Expected_Prefix'Length; begin Assert (Success, Msg, Location); if not Success then if Right'Length > 0 then IO.Put (Expected_Prefix); Put_Indented (Indent, Right); IO.New_Line; else IO.Put_Line ("expected empty string"); end if; if Left'Length > 0 then IO.Put (Got_Prefix); Put_Indented (Indent, Left); IO.New_Line; else IO.Put_Line ("got empty string"); end if; end if; end Assert; ------------ -- Assert -- ------------ procedure Assert (Left : Wide_String; Right : UTF8.UTF_8_String; Msg : String := ""; Location : String := SI.Source_Location) is UTF_Left : constant UTF8.UTF_8_String := UTF.Encode (Left); begin Assert (UTF_Left, Right, Msg, Location); end Assert; ------------ -- Assert -- ------------ procedure Assert (Left : Integer; Right : Integer; Msg : String := ""; Location : String := SI.Source_Location) is Success : constant Boolean := Left = Right; begin Assert (Success, Msg, Location); if not Success then IO.Put_Line ("expected: " & Right'Img); IO.Put_Line ("got: " & Left'Img); end if; end Assert; ------------ -- Assert -- ------------ procedure Assert (Left, Right : VFS.Virtual_File; Msg : String := ""; Location : String := SI.Source_Location) is use type VFS.Virtual_File; Success : constant Boolean := Left = Right; begin Assert (Success, Msg, Location); if not Success then IO.Put_Line ("expected: " & VFS.Display_Full_Name (Right)); IO.Put_Line ("got: " & VFS.Display_Full_Name (Left)); end if; end Assert; --------------------- -- Assert_Inferior -- --------------------- procedure Assert_Inferior (Left : Time; Right : Time; Msg : String := ""; Location : String := SI.Source_Location) is Success : constant Boolean := Left < Right; begin Assert (Success, Msg, Location); if not Success then IO.Put_Line ("left: " & Image (Left) & " (" & To_Unix_Nano_Time (Left)'Img & ")"); IO.Put_Line ("right: " & Image (Right) & " (" & To_Unix_Nano_Time (Right)'Img & ")"); end if; end Assert_Inferior; ------------ -- Report -- ------------ function Report return Natural is begin if Final_Status = 0 then IO.Put_Line ("<=== TEST PASSED ===>"); else IO.Put_Line ("<=== TEST FAILED ===>"); end if; return Final_Status; end Report; end Test_Assert; gnatcoll-core-21.0.0/testsuite/tests/0000755000175000017500000000000013661715457017377 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/utils/0000755000175000017500000000000013661715457020537 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/utils/add_search_path/0000755000175000017500000000000013743647711023626 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/utils/add_search_path/test.adb0000644000175000017500000000222413661715457025257 0ustar nicolasnicolaswith GNATCOLL.Utils; with Test_Assert; with Ada.Environment_Variables; with GNAT.OS_Lib; function Test return Integer is package GU renames GNATCOLL.Utils; package A renames Test_Assert; package Env renames Ada.Environment_Variables; package OS renames GNAT.OS_Lib; Sep : constant Character := OS.Path_Separator; begin Env.Clear ("GNATCOLL_PATH"); GU.Add_Search_Path ("GNATCOLL_PATH", "/dummy"); A.Assert (Env.Value ("GNATCOLL_PATH", ""), "/dummy"); GU.Add_Search_Path ("GNATCOLL_PATH", "/dummy2"); A.Assert (Env.Value ("GNATCOLL_PATH", ""), "/dummy2" & OS.Path_Separator & "/dummy"); GU.Add_Search_Path ("GNATCOLL_PATH", "/dummy"); A.Assert (Env.Value ("GNATCOLL_PATH", ""), "/dummy" & OS.Path_Separator & "/dummy2"); Env.Set ("GNATCOLL_PATH", "." & Sep & Sep); GU.Add_Search_Path ("GNATCOLL_PATH", "/dummy"); A.Assert (Env.Value ("GNATCOLL_PATH", ""), "/dummy" & OS.Path_Separator & "." & Sep & Sep); GU.Add_Search_Path ("GNATCOLL_PATH", ""); A.Assert (Env.Value ("GNATCOLL_PATH", ""), "/dummy" & OS.Path_Separator & "." & Sep & Sep); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/utils/add_search_path/test.yaml0000644000175000017500000000016013743647711025466 0ustar nicolasnicolastitle: GNATCOLL.Utils.Add_Search_Path control: - [XFAIL, "env.valgrind", "Known memory leak: see S912-005"] gnatcoll-core-21.0.0/testsuite/tests/utils/str_checks/0000755000175000017500000000000013661715457022667 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/utils/str_checks/test.adb0000644000175000017500000001133413661715457024320 0ustar nicolasnicolaswith GNATCOLL.Utils; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Characters.Latin_1; use Ada.Characters.Latin_1; with Test_Assert; with Ada.Text_IO; function Test return Integer is package A renames Test_Assert; package IO renames Ada.Text_IO; procedure Test_Equality; procedure Test_Is_Whitespace; procedure Test_Starts_With; procedure Test_Ends_With; procedure Test_Is_Blank_Line; procedure Test_Equality is S1 : constant String := "abcdefg"; S2 : constant String := To_Upper (S1); S3 : constant String := "hijklmn"; S4 : constant String := S1 & S3; Empty : constant String := ""; begin for CS in Boolean loop IO.Put_Line ("Test Equal with case sensitity set to: " & CS'Image); A.Assert (GNATCOLL.Utils.Equal (Empty, "", Case_Sensitive => CS) = True, Msg => "empty string"); A.Assert (GNATCOLL.Utils.Equal (S1, S1, Case_Sensitive => CS) = True, Msg => "exact same string"); A.Assert (GNATCOLL.Utils.Equal (S1, S2, Case_Sensitive => CS) = not CS, Msg => "compare to To_Upper copy"); A.Assert (GNATCOLL.Utils.Equal (S1, S3, Case_Sensitive => CS) = False, Msg => "differnet strings"); A.Assert (GNATCOLL.Utils.Equal (S4, S1, Case_Sensitive => CS) = False, Msg => "S4 compare to its substring S1"); end loop; IO.Put_Line ("Test Case_Insensitive_Equal"); A.Assert (GNATCOLL.Utils.Case_Insensitive_Equal (S1, S1) = True, Msg => "exact same string"); A.Assert (GNATCOLL.Utils.Case_Insensitive_Equal (S1, S2) = True, Msg => "compare to To_Upper copy"); A.Assert (GNATCOLL.Utils.Case_Insensitive_Equal (S1, S3) = False, Msg => "different string"); end Test_Equality; procedure Test_Is_Whitespace is subtype Whitespace_Type is Character with Static_Predicate => Whitespace_Type in ' ' | LF | CR; begin IO.Put_Line ("Test Is_Whitespace"); for Car in Whitespace_Type loop A.Assert (GNATCOLL.Utils.Is_Whitespace (Car) = True, Msg => Character'Image (Car) & " is whitespace"); end loop; A.Assert (GNATCOLL.Utils.Is_Whitespace ('a') = False, Msg => "is whitespace false"); A.Assert (GNATCOLL.Utils.Is_Whitespace (NUL) = False, Msg => "is whitespace empty"); end Test_Is_Whitespace; procedure Test_Starts_With is begin IO.Put_Line ("Test Starts_With"); A.Assert (GNATCOLL.Utils.Starts_With ("ABCD", "ABC") = True, Msg => "starts_with true"); A.Assert (GNATCOLL.Utils.Starts_With ("ABCD", "") = True, Msg => "starts_with empty => always true"); A.Assert (GNATCOLL.Utils.Starts_With ("ABCD", "BCD") = False, Msg => "starts_with false"); A.Assert (GNATCOLL.Utils.Starts_With ("ABCD", "BCDEFGH") = False, Msg => "starts_with false : pattern too long"); end Test_Starts_With; procedure Test_Ends_With is begin IO.Put_Line ("Test Ends_With"); A.Assert (GNATCOLL.Utils.Ends_With ("ABCD", "BCD") = True, Msg => "ends_with true"); A.Assert (GNATCOLL.Utils.Ends_With ("ABCD", "") = True, Msg => "ends_with empty => always true"); A.Assert (GNATCOLL.Utils.Ends_With ("ABCD", "ABC") = False, Msg => "ends_with false"); A.Assert (GNATCOLL.Utils.Ends_With ("ABCD", "ABCDEFG") = False, Msg => "ends_with false : pattern too long"); end Test_Ends_With; procedure Test_Is_Blank_Line is S1 : constant String := " " & ASCII.CR; S2 : constant String := "AAAAA"; S3 : constant String := S1 & ASCII.CR & ASCII.LF & S2; begin IO.Put_Line ("Test Is_Blank_Line"); A.Assert (GNATCOLL.Utils.Is_Blank_Line ("", 0) = True, Msg => "empty string and Index = 0"); A.Assert (GNATCOLL.Utils.Is_Blank_Line (S1, 0) = True, Msg => "only blank + Index = 0"); A.Assert (GNATCOLL.Utils.Is_Blank_Line (S2, 0) = False, Msg => "Not blank + Index = 0"); A.Assert (GNATCOLL.Utils.Is_Blank_Line ("", 5) = True, Msg => "empty string, Index > 0"); A.Assert (GNATCOLL.Utils.Is_Blank_Line (S3, S1'Length + 3) = False, Msg => "not blank"); end Test_Is_Blank_Line; begin IO.New_Line; Test_Equality; IO.New_Line; Test_Is_Whitespace; IO.New_Line; Test_Starts_With; IO.New_Line; Test_Ends_With; IO.New_Line; Test_Is_Blank_Line; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/utils/str_checks/test.yaml0000644000175000017500000000036213661715457024533 0ustar nicolasnicolastitle: "GNATCOLL.Utils string checks" description: > "Test of checks on Strings available in GNATCOLL.Utils. Only involves String type, and no modifications of the String in entry, handle the whole string or character (no split)" gnatcoll-core-21.0.0/testsuite/tests/utils/str_proc/0000755000175000017500000000000013661715457022372 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/utils/str_proc/test.adb0000644000175000017500000001152613661715457024026 0ustar nicolasnicolaswith GNATCOLL.Utils; with GNAT.Strings; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Characters.Handling; use Ada.Characters.Handling; with System.Assertions; with Test_Assert; with Ada.Text_IO; function Test return Integer is package A renames Test_Assert; package IO renames Ada.Text_IO; procedure Test_Replace; procedure Test_Capitalize; procedure Test_Join; procedure Test_Replace is S : constant String := "This adacore is adacore a adacore String"; SS : constant String := "This something is something a something String"; S2 : Unbounded_String := To_Unbounded_String (S); S3 : constant String := "This is a String"; begin -- Test Replace with String -- IO.Put_Line ("Test Replace with Strings"); declare S4 : constant String := GNATCOLL.Utils.Replace (S, "adacore ", ""); S5 : constant String := GNATCOLL.Utils.Replace (S, "adacore", "something"); S6 : constant String := GNATCOLL.Utils.Replace (S, "something", "something_else"); begin A.Assert (S4 = S3, Msg => "replace valid pattern into empty string"); A.Assert (S5 = SS, Msg => "replace valid pattern"); A.Assert (S6 = S, Msg => "replace 'absent' pattern into other word"); declare pragma Warnings (Off); S7 : String (1 .. S'Length); pragma Warning (On); begin S7 := GNATCOLL.Utils.Replace (S, "", " something"); A.Assert (False, Msg => "replace failed raising exception or pre-cond"); exception when System.Assertions.Assert_Failure => A.Assert (True, Msg => "replace raised assertion failure"); when Ada.Strings.Pattern_Error => A.Assert (True, Msg => "replace raised exception (no assertions)"); end; end; -- Test Replace with Unbounded String -- IO.Put_Line ("Test Replace with Unbounded Strings"); GNATCOLL.Utils.Replace (S2, "adacore ", ""); A.Assert (To_String (S2) = S3, Msg => "replace valid pattern into empty string"); GNATCOLL.Utils.Replace (S2, "something", "something_else"); A.Assert (To_String (S2) = S3, Msg => "replace valid pattern"); declare begin GNATCOLL.Utils.Replace (S2, "", " something"); A.Assert (False, Msg => "replace failed raising exception or pre-cond"); exception when System.Assertions.Assert_Failure => A.Assert (True, Msg => "replace raised assertion failure"); when Ada.Strings.Pattern_Error => A.Assert (True, Msg => "replace raised exception (no assertions)"); end; end Test_Replace; procedure Test_Capitalize is S1 : constant String := "abcdefg"; S2 : constant String := To_Upper (S1); S3 : constant String := "Abcdefg"; Empty : constant String := ""; begin IO.Put_Line ("Test Capitalize"); A.Assert (GNATCOLL.Utils.Capitalize (Empty) = "", Msg => "empty string"); A.Assert (GNATCOLL.Utils.Capitalize (S1) = S3, Msg => "capitalize from all lower"); A.Assert (GNATCOLL.Utils.Capitalize (S2) = S3, Msg => "capitalize from all upper"); A.Assert (GNATCOLL.Utils.Capitalize ("abCD__ef___GHi_j_k__l") = "Abcd_Ef_Ghi_J_K_L", Msg => "capitalize when several '_'"); A.Assert (GNATCOLL.Utils.Capitalize ("abCD/ef//GHi/j/k////l") = "Abcd_Ef__Ghi_J_K____L", Msg => "capitalize when several '/'"); A.Assert (GNATCOLL.Utils.Capitalize ("c++abcd") = "Cppabcd", Msg => "capitalize with '+'"); A.Assert (GNATCOLL.Utils.Capitalize ("c--abcd") = "C__Abcd", Msg => "capitalize with '-'"); A.Assert (GNATCOLL.Utils.Capitalize ("abc'def''ghijkl'''mno") = "Abc_Def__Ghijkl___Mno", Msg => "capitalize with "); A.Assert (GNATCOLL.Utils.Capitalize ("\abc?def?\?g\hijkl???mno") = "\abcUdefU\Ug\hijklUUUmno", Msg => "capitalize with '?' and '\'"); end Test_Capitalize; procedure Test_Join is S1 : constant String := ","; S2 : aliased String := "adacore"; S : constant String := S2 & S1 & S2 & S1 & S2 & S1 & S2 & S1 & S2; SL : GNAT.Strings.String_List (1 .. 5) := (others => S2'Unchecked_Access); begin A.Assert (GNATCOLL.Utils.Join (S1, SL) = S, Msg => "simple join"); end Test_Join; begin IO.New_Line; IO.Put_Line ("GNATCOLL.UTILS"); IO.New_Line; Test_Replace; IO.New_Line; Test_Capitalize; IO.New_Line; Test_Join; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/utils/str_proc/test.yaml0000644000175000017500000000014613661715457024236 0ustar nicolasnicolastitle: GNATCOLL.Utils strings processing description: Test for GNATCOLL.Utils processing of string gnatcoll-core-21.0.0/testsuite/tests/utils/str_query/0000755000175000017500000000000013661715457022574 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/utils/str_query/test.adb0000644000175000017500000002742213661715457024232 0ustar nicolasnicolaswith GNATCOLL.Utils; with Test_Assert; with Ada.Text_IO; function Test return Integer is package A renames Test_Assert; package IO renames Ada.Text_IO; procedure Test_Image; procedure Test_Find_Char; procedure Test_Line_Start; procedure Test_Next_Line; procedure Test_Line_End; procedure Test_Previous_Line; procedure Test_Image is S1 : constant String := "005"; S2 : constant String := '+' & S1; S3 : constant String := "-00000" & S1; Int1 : constant Integer := 5; Int2 : constant Integer := -5; S4 : constant String := "- 455"; Int3 : constant Integer := -455; begin IO.Put_Line ("Test Image"); A.Assert (GNATCOLL.Utils.Image (Int1, Int1'Image'Length, Padding => ' ') = Int1'Image, Msg => "positive, mimic attribute Image"); A.Assert (GNATCOLL.Utils.Image (Int1, S1'Length) = S1, Msg => "positive, several 0 (default padding)"); A.Assert (GNATCOLL.Utils.Image (Int1, S1'Length, Force_Sign => True) = S2, Msg => "positive, several 0, force sign"); A.Assert (GNATCOLL.Utils.Image (Int2, S3'Length - 1) = S3, Msg => "negative, several 0"); A.Assert (GNATCOLL.Utils.Image (Int3, S4'Length - 1, Padding => ' ', Force_Sign => True) = S4, Msg => "negative, several ' ', force sign"); end Test_Image; procedure Test_Find_Char is Str : String (1 .. Character'Pos (Character'Last) + 1); Str2 : constant String := "AAAAAAAAAAAAAAAAAAA"; Str3 : constant String := Str2 & ASCII.LF & "BBBBB"; begin IO.Put_Line ("Test Find_Char"); for Char in Character loop Str (Character'Pos (Char) + 1) := Char; end loop; for Char in Character loop A.Assert (GNATCOLL.Utils.Find_Char (Str, Char) = Character'Pos (Char) + 1, Msg => "find all char in in all char string"); end loop; A.Assert (GNATCOLL.Utils.Find_Char (Str2, 'B') = Str2'Last + 1, Msg => "not found"); A.Assert (GNATCOLL.Utils.Find_Char (Str2, 'A') = Str2'First, Msg => "several occurence => take first"); A.Assert (GNATCOLL.Utils.Find_Char ("", 'A') = 1, Msg => "in empty string = found at begining"); A.Assert (GNATCOLL.Utils.Find_Char (Str2, ASCII.NUL) = Str2'Last + 1, Msg => "null char in string = not found"); IO.Put_Line ("Test EOL"); A.Assert (GNATCOLL.Utils.EOL (Str2) = Str2'Last + 1, Msg => "EOL not found"); A.Assert (GNATCOLL.Utils.EOL (Str3) = Str2'Length + 1, Msg => "first occurence of EOL"); A.Assert (GNATCOLL.Utils.EOL ("") = 1, Msg => "EOL in empty string"); end Test_Find_Char; procedure Test_Line_Start is S1 : constant String := "This is line 1"; S2 : constant String := "This is the second line"; CRLF : constant String := ASCII.CR & ASCII.LF; LFCR : constant String := ASCII.LF & ASCII.CR; S_1LF : constant String := S1 & ASCII.LF & S2; S_1CR : constant String := S1 & ASCII.CR & S2; S_6LF : constant String := S1 & ASCII.LF & ASCII.LF & ASCII.LF & ASCII.LF & ASCII.LF & ASCII.LF & S2; S_6CR : constant String := S1 & ASCII.CR & ASCII.CR & ASCII.CR & ASCII.CR & ASCII.CR & ASCII.CR & S2; S_6CRLF : constant String := S1 & CRLF & CRLF & CRLF & CRLF & CRLF & CRLF & S2; S_6LFCR : constant String := S1 & LFCR & LFCR & LFCR & LFCR & LFCR & LFCR & S2; begin IO.Put_Line ("Test Line_Start"); A.Assert (GNATCOLL.Utils.Line_Start (S1, 0) = 0, Msg => "P equals 0 (under S1 lower bound)"); A.Assert (GNATCOLL.Utils.Line_Start (S1, S1'Last + 100) = 1, Msg => "P over S1 upper bound"); A.Assert (GNATCOLL.Utils.Line_Start (S1, 1) = 1, Msg => "P on first char"); A.Assert (GNATCOLL.Utils.Line_Start (S1, S1'Last) = 1, Msg => "one line, P on last char"); A.Assert (GNATCOLL.Utils.Line_Start (S1, S1'Length - 2) = 1, Msg => "one line, P in string"); A.Assert (GNATCOLL.Utils.Line_Start (S_1LF, S1'Length) = 1, Msg => "P is before an LF char (no CR)"); A.Assert (GNATCOLL.Utils.Line_Start (S_1LF, S1'Length + 2) = S1'Length + 2, Msg => "P is after an LF char (no CR)"); A.Assert (GNATCOLL.Utils.Line_Start (S_1LF, S1'Length + 1) = 1, Msg => "P is on a LF char (no CR)"); A.Assert (GNATCOLL.Utils.Line_Start (S_6LF, S1'Length + 6) = S1'Length + 6, Msg => "P is on 6th LF char (no CR)"); A.Assert (GNATCOLL.Utils.Line_Start (S_6LF, S1'Length + 7) = S1'Length + 7, Msg => "P is after last LF char (no CR)"); A.Assert (GNATCOLL.Utils.Line_Start (S_1CR, S1'Length) = 1, Msg => "P is before a CR char (no LF)"); A.Assert (GNATCOLL.Utils.Line_Start (S_1CR, S1'Length + 2) = S1'Length + 2, Msg => "P is after a CR char"); A.Assert (GNATCOLL.Utils.Line_Start (S_1CR, S1'Length + 1) = 1, Msg => "P is on a CR char"); A.Assert (GNATCOLL.Utils.Line_Start (S_6CR, S1'Length + 6) = S1'Length + 6, Msg => "P is on 6th CR char (no CR)"); A.Assert (GNATCOLL.Utils.Line_Start (S_6CR, S1'Length + 7) = S1'Length + 7, Msg => "P is after last CR char (no CR)"); A.Assert (GNATCOLL.Utils.Line_Start (S_6CRLF, S1'Length + 2) = 1, Msg => "P on the LF of a CRLF"); A.Assert (GNATCOLL.Utils.Line_Start (S_6LFCR, S1'Length + 2) = S1'Length + 2, Msg => "P on the CR of a LFCR"); A.Assert (GNATCOLL.Utils.Line_Start (S_6CRLF, S1'Length + 5) = S1'Length + 5, Msg => "P on the CR of the 3rd CRLF"); A.Assert (GNATCOLL.Utils.Line_Start (S_6CRLF, S1'Length + 6) = S1'Length + 5, Msg => "P on the LF of the 3rd CRLF"); A.Assert (GNATCOLL.Utils.Line_Start (S_6LFCR, S1'Length + 5) = S1'Length + 4, Msg => "P on the LF of the 3rd LFCR"); A.Assert (GNATCOLL.Utils.Line_Start (S_6LFCR, S1'Length + 6) = S1'Length + 6, Msg => "P on the CR of the 3rd LFCR"); A.Assert (GNATCOLL.Utils.Line_Start (S_6CRLF, S1'Length + 13) = S1'Length + 13, Msg => "P after last CRLF"); A.Assert (GNATCOLL.Utils.Line_Start (CRLF, 2) = CRLF'First, Msg => "P on the LF of a string only made of 1 CRLF"); A.Assert (GNATCOLL.Utils.Line_Start (LFCR, 2) = CRLF'First + 1, Msg => "P on the CR of a string only made of 1 LFCR"); end Test_Line_Start; procedure Test_Next_Line is S1 : constant String := "This is line 1"; S2 : constant String := "This is the second line"; S3 : constant String := "This is the third line"; CRLF : constant String := ASCII.CR & ASCII.LF; LFCR : constant String := ASCII.LF & ASCII.CR; S_CRLF : constant String := S1 & CRLF & S2 & CRLF & S3; S_LFCR : constant String := S1 & LFCR & S2 & LFCR & S3; begin IO.Put_Line ("Test Next_Line"); A.Assert (GNATCOLL.Utils.Next_Line (S1, 0) = S1'Length, Msg => "P equals 0 (under S1 lower bound)"); A.Assert (GNATCOLL.Utils.Next_Line (S1, S1'Last + 100) = S1'Length, Msg => "P over S1 upper bound"); A.Assert (GNATCOLL.Utils.Next_Line (S1, S1'Length - 2) = S1'Length, Msg => "one line, P in string"); A.Assert (GNATCOLL.Utils.Next_Line (S_CRLF, S1'Length + 1) = S1'Length + 3, Msg => "P on a CR of a CRLF (string continue after that)"); A.Assert (GNATCOLL.Utils.Next_Line (S_CRLF, S1'Length + 2) = S1'Length + 3, Msg => "P on an LF of a CRLF (string continue after that)"); A.Assert (GNATCOLL.Utils.Next_Line (S_LFCR, S1'Length + 1) = S1'Length + 2, Msg => "P on a LF of a LFCR (string continues after that)"); A.Assert (GNATCOLL.Utils.Next_Line (S_LFCR, S1'Length + 2) = S1'Length + 2 + S2'Length + 2, Msg => "P on a CR of a LFCR (string continues after that)"); end Test_Next_Line; procedure Test_Previous_Line is S1 : constant String := "This is line 1"; S2 : constant String := "This is the second line"; S3 : constant String := "This is the third line"; CRLF : constant String := ASCII.CR & ASCII.LF; LFCR : constant String := ASCII.LF & ASCII.CR; S_CRLF : constant String := S1 & CRLF & S2 & CRLF & S3; S_LFCR : constant String := S1 & LFCR & S2 & LFCR & S3; begin IO.Put_Line ("Test Previous_Line"); A.Assert (GNATCOLL.Utils.Previous_Line (S1, 0) = 1, Msg => "P equals 0 (under S1 lower bound)"); A.Assert (GNATCOLL.Utils.Previous_Line (S1, S1'Last + 100) = S1'First, Msg => "P over S1 upper bound"); A.Assert (GNATCOLL.Utils.Previous_Line (S1, S1'Length - 2) = S1'First, Msg => "one line, P in string"); A.Assert (GNATCOLL.Utils.Previous_Line (S_CRLF, S1'Length + 5) = S1'First, Msg => "In a string, after a CRLF"); A.Assert (GNATCOLL.Utils.Previous_Line (S_LFCR, S1'Length + 5) = S1'Length + 2, Msg => "In a string, after a LFCR"); A.Assert (GNATCOLL.Utils.Previous_Line (S_LFCR, S1'Length + 1) = S1'First, Msg => "P on a LF of a string with LFCR"); A.Assert (GNATCOLL.Utils.Previous_Line (S_LFCR, S1'Length + 2) = S1'First, Msg => "P on a CR of a string with LFCR"); A.Assert (GNATCOLL.Utils.Previous_Line (S_CRLF, S1'Length + 1) = S1'First, Msg => "P on a CR of a string with CRLF"); A.Assert (GNATCOLL.Utils.Previous_Line (S_CRLF, S1'Length + 2) = S1'First, Msg => "P on an LF of a string with CRLF"); end Test_Previous_Line; procedure Test_Line_End is S1 : constant String := "This is line 1"; S2 : constant String := "This is the second line"; CRLF : constant String := ASCII.CR & ASCII.LF; S_6LF : constant String := S1 & ASCII.LF & ASCII.LF & ASCII.LF & ASCII.LF & ASCII.LF & ASCII.LF & S2; S_6CR : constant String := S1 & ASCII.CR & ASCII.CR & ASCII.CR & ASCII.CR & ASCII.CR & ASCII.CR & S2; begin IO.Put_Line ("Test Line_End"); A.Assert (GNATCOLL.Utils.Line_End (S1, 0) = S1'Length, Msg => "P equals 0 (under S1 lower bound)"); A.Assert (GNATCOLL.Utils.Line_End (S1, S1'Last + 100) = S1'Length, Msg => "P over S1 upper bound"); A.Assert (GNATCOLL.Utils.Line_End (S_6LF, S1'First + 1) = S1'Length, Msg => "P is before an LF char (no CR)"); A.Assert (GNATCOLL.Utils.Line_End (S_6CR, S1'First + 1) = S1'Length, Msg => "P is before an CR char (no LF)"); -- ??? Line_End of "CRLF" on 1st caracter is 0 A.Assert (GNATCOLL.Utils.Line_End (CRLF, 1) = CRLF'First - 1, Msg => "P is the CR of a CRLF string"); A.Assert (GNATCOLL.Utils.Line_End (CRLF, 2) = 2 - 1, Msg => "P is the LF of a CRLF string"); end Test_Line_End; begin IO.New_Line; Test_Image; IO.New_Line; Test_Find_Char; IO.New_Line; Test_Line_Start; IO.New_Line; Test_Line_End; IO.New_Line; Test_Next_Line; IO.New_Line; Test_Previous_Line; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/utils/str_query/test.yaml0000644000175000017500000000034213661715457024436 0ustar nicolasnicolastitle: "GNATCOLL.Utils string queries" description: > "Test of queries on Strings available in GNATCOLL.Utils. Only involves String type, and no modifications of the String in entry. Can involve split of strings." gnatcoll-core-21.0.0/testsuite/tests/utils/join_path/0000755000175000017500000000000013661715457022512 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/utils/join_path/test.adb0000644000175000017500000000112613661715457024141 0ustar nicolasnicolaswith GNATCOLL.Utils; with Test_Assert; with GNAT.OS_Lib; function Test return Integer is package GU renames GNATCOLL.Utils; package A renames Test_Assert; Sep : constant Character := GNAT.OS_Lib.Directory_Separator; begin A.Assert (GU.Join_Path ("/dummy", "lib"), "/dummy" & Sep & "lib"); A.Assert (GU.Join_Path ("/dummy", "/foo", "bin"), "/foo" & Sep & "bin"); A.Assert (GU.Join_Path ("/foo", "bar1", "bar2", "bar3", "bar4"), "/foo" & Sep & "bar1" & Sep & "bar2" & Sep & "bar3" & Sep & "bar4"); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/utils/join_path/test.yaml0000644000175000017500000000004013661715457024347 0ustar nicolasnicolastitle: GNATCOLL.Utils.Path_Join gnatcoll-core-21.0.0/testsuite/tests/utils/executable_path/0000755000175000017500000000000013661715457023674 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/utils/executable_path/test.adb0000644000175000017500000000215013661715457025321 0ustar nicolasnicolaswith GNATCOLL.Utils; with Test_Assert; with Ada.Environment_Variables; with Ada.Directories; with GNAT.OS_Lib; function Test return Integer is package A renames Test_Assert; package GU renames GNATCOLL.Utils; package Env renames Ada.Environment_Variables; package Dir renames Ada.Directories; package OS renames GNAT.OS_Lib; begin declare Exe_Path : constant String := GU.Executable_Path; Process_Status : Integer; Success : Boolean; begin A.Assert (OS.Is_Regular_File (Exe_Path)); if not OS.Is_Directory ("Bin") then Dir.Create_Directory ("Bin"); OS.Copy_File (Exe_Path, "Bin", Success, Preserve => OS.Full); Process_Status := OS.Spawn ("Bin/test", Args => (1 .. 0 => null)); A.Assert (Process_Status = 0); end if; Dir.Set_Directory (".."); Env.Set ("PATH", ""); A.Assert (Exe_Path, GU.Executable_Path, "Ensure that executable path is not impacted by " & "environment changes"); A.Assert (OS.Is_Directory (GU.Executable_Location)); end; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/utils/executable_path/test.yaml0000644000175000017500000000004613661715457025537 0ustar nicolasnicolastitle: GNATCOLL.Utils.Executable_Path gnatcoll-core-21.0.0/testsuite/tests/coders/0000755000175000017500000000000013661715457020656 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/coders/test_streams.adb0000644000175000017500000000316213661715457024045 0ustar nicolasnicolaspackage body Test_Streams is ---------- -- Read -- ---------- overriding procedure Read (Stream : in out Stream_Type; Item : out Stream_Element_Array; Last : out Stream_Element_Offset) is Length : constant Integer := Natural'Min (Item'Length, Stream.Buffer.Length - Stream.Position); Target : String (1 .. Integer'Max (Length, 0)); for Target'Address use Item'Address; begin if Target = "" then Last := Item'First - 1; return; end if; Target := To_String (Stream.Buffer.Slice (Stream.Position + 1, Stream.Position + Length)); Stream.Position := Stream.Position + Length; Last := Item'First + Stream_Element_Offset (Length) - 1; end Read; ----------- -- Write -- ----------- overriding procedure Write (Stream : in out Stream_Type; Item : Stream_Element_Array) is Source : String (1 .. Item'Length); for Source'Address use Item'Address; begin Stream.Buffer.Append (Source); end Write; ----------- -- Clear -- ----------- procedure Clear (Stream : in out Stream_Type) is begin Stream.Buffer.Clear; Stream.Reset; end Clear; ----------- -- Reset -- ----------- procedure Reset (Stream : in out Stream_Type) is begin Stream.Position := 0; end Reset; ----------- -- Slice -- ----------- function Slice (Stream : Stream_Type; Low : Positive; High : Natural) return String is begin return To_String (Stream.Buffer.Slice (Low, High)); end Slice; end Test_Streams; gnatcoll-core-21.0.0/testsuite/tests/coders/test.adb0000644000175000017500000001250113661715457022304 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- -- Test for GNATCOLL.Coders with Ada.Streams; use Ada.Streams; with GNATCOLL.Coders.Streams; use GNATCOLL.Coders; with GNATCOLL.Coders.Base64; with Tb64; with Test_Streams; with Test_Assert; function Test return Integer is package A renames Test_Assert; Stream : aliased Streams.Stream_Type; Encoder : aliased Base64.Encoder_Type; Decoder : aliased Base64.Decoder_Type; Buffer : Stream_Element_Array (0 .. 511); function Test_Iteration (Read_Ends_By : Streams.End_Of_Input_Method; Tail : Stream_Element_Offset) return Boolean; function Test_Iteration (Read_Ends_By : Streams.End_Of_Input_Method; Tail : Stream_Element_Offset) return Boolean is subtype Middle_Range is Stream_Element_Offset range 241 .. 270; TS : aliased Test_Streams.Stream_Type; Last : Stream_Element_Offset; begin Encoder.Initialize (Wrap => 76); Decoder.Initialize; Stream.Initialize (Read_Coder => Decoder'Unchecked_Access, Write_Coder => Encoder'Unchecked_Access, Read_From => TS'Unchecked_Access, Write_To => TS'Unchecked_Access, Read_Ends_By => Read_Ends_By); for M in Middle_Range loop Stream.Write (Buffer (Buffer'First .. M)); Stream.Write (Buffer (M + 1 .. Buffer'Last)); end loop; Stream.Write (Buffer (Buffer'First .. Tail)); Stream.Flush (Finish); declare Result : Stream_Element_Array (Buffer'Range); begin for M in reverse Middle_Range loop Stream.Read (Result (Result'First .. M), Last); if Last /= M then A.Assert (False, "Unexpected" & Last'Img & " /= " & M'Img); return False; end if; Stream.Read (Result (M + 1 .. Result'Last), Last); if Last /= Buffer'Last then A.Assert (False, "Unexpected" & Last'Img & " /= " & Buffer'Last'Img & " at" & M'Img); return False; end if; if Result (Buffer'Range) /= Buffer then A.Assert (False, "Result (Buffer'Range) /= Buffer at " & M'Img & ' ' & TS.Slice (1, 64)); for J in Buffer'Range loop if Result (J) /= Buffer (J) then A.Assert (Result (J)'Img, Buffer (J)'Img, "at index " & J'Img); return False; end if; end loop; return False; end if; end loop; Stream.Read (Result, Last); if Last /= Tail then A.Assert (False, "Unexpected" & Last'Img & " /= " & Tail'Img); return False; end if; if Result (Result'First .. Tail) /= Buffer (Buffer'First .. Tail) then A.Assert (False, "Tail failure"); for J in Result'First .. Tail loop if Result (J) /= Buffer (J) then A.Assert (Result (J)'Img, Buffer (J)'Img, "at index " & J'Img); return False; end if; end loop; return False; end if; end; return True; end Test_Iteration; begin for J in Buffer'Range loop Buffer (J) := Stream_Element'Mod (J); end loop; Main_Loop : for End_Mode in Streams.End_Of_Input_Method loop for Tail in Stream_Element_Offset range 1 .. 255 loop exit Main_Loop when not Test_Iteration (End_Mode, Tail); end loop; end loop Main_Loop; Tb64; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/coders/test.yaml0000644000175000017500000000004613661715457022521 0ustar nicolasnicolasdescription: Test for GNATCOLL.Coders gnatcoll-core-21.0.0/testsuite/tests/coders/test.gpr0000644000175000017500000000061213661715457022346 0ustar nicolasnicolaswith "gnatcoll"; project Test is for Main use ("test.adb"); for Source_Dirs use (".", "../../support"); for Object_Dir use "obj"; package Compiler is for Switches ("Ada") use ("-g", "-gnateE"); end Compiler; package Linker is for Switches ("Ada") use ("-g"); end Linker; package Binder is for Switches ("Ada") use ("-E"); end Binder; end Test; gnatcoll-core-21.0.0/testsuite/tests/coders/tb64.adb0000644000175000017500000001051113661715457022103 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- -- Test base64 encoder and decoder with Ada.Streams; use Ada.Streams; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Text_IO; use Ada.Text_IO; with GNATCOLL.Coders.Base64; use GNATCOLL.Coders; with GNATCOLL.Email.Utils; use GNATCOLL.Email.Utils; with Test_Assert; with GNAT.Random_Numbers; use GNAT.Random_Numbers; procedure TB64 is package A renames Test_Assert; Encoder : Base64.Encoder_Type; Decoder : Base64.Decoder_Type; Sample : Stream_Element_Array (1 .. 4096); Text : String (1 .. Sample'Length); for Text'Address use Sample'Address; Coded : Stream_Element_Array (1 .. Sample'Last * 5 / 3 + 1); Coded_S : String (1 .. Coded'Length); for Coded_S'Address use Coded'Address; Coded_U : Unbounded_String; Result : Stream_Element_Array (Sample'First .. Sample'Last + 1); Result_U : Unbounded_String; In_Last : Stream_Element_Offset; Cod_Last : Stream_Element_Offset; Res_Last : Stream_Element_Offset; Gen : Generator; Printable : constant Boolean := False; begin Reset (Gen); Coded (1) := Stream_Element'Val (Character'Pos ('a')); for J in Sample'Range loop if Printable then Sample (1 .. J) := Coded (1 .. J); else Sample (J) := Stream_Element (Stream_Element'Mod (Integer'(Random (Gen)))); end if; Encoder.Initialize (Wrap => (if Integer'(Random (Gen)) rem 8 = 0 then 0 else Random (Gen) rem 70 + 20), Mode => Base64.Base64_Mode'Val (Integer'(Random (Gen)) rem (Base64.Base64_Mode'Pos (Base64.Base64_Mode'Last) + 1))); Decoder.Initialize; Encoder.Transcode (In_Data => Sample (1 .. J), In_Last => In_Last, Out_Data => Coded, Out_Last => Cod_Last, Flush => Finish); A.Assert (In_Last = J, "In_Last /= J"); Decoder.Transcode (In_Data => Coded (1 .. Cod_Last), In_Last => In_Last, Out_Data => Result, Out_Last => Res_Last, Flush => Finish); A.Assert (In_Last = Cod_Last, "In_Last /= Cod_Last"); A.Assert (J = Res_Last, "J /= Res_Last"); A.Assert (Sample (1 .. J) = Result (1 .. J), "Sample /= Result"); Encoder.Close; Decoder.Close; Base64_Encode (Text (1 .. Integer (J)), "", Max_Block_Len => Random (Gen) rem 64 + 16, Result => Coded_U); Base64_Decode (To_String (Coded_U), Result_U); A.Assert (Result_U = Text (1 .. Integer (J)), "Result_U /= Text (1 .. Integer (J))"); end loop; end TB64; gnatcoll-core-21.0.0/testsuite/tests/coders/test_streams.ads0000644000175000017500000000166313661715457024072 0ustar nicolasnicolaswith Ada.Streams; use Ada.Streams; with GNATCOLL.Strings; use GNATCOLL.Strings; package Test_Streams is type Stream_Type is new Root_Stream_Type with private; -- Stream reading the data which was wrote there before overriding procedure Read (Stream : in out Stream_Type; Item : out Stream_Element_Array; Last : out Stream_Element_Offset); overriding procedure Write (Stream : in out Stream_Type; Item : Stream_Element_Array); procedure Reset (Stream : in out Stream_Type); -- Reset read opsition to the start of data procedure Clear (Stream : in out Stream_Type); -- Clear all internal written data from stream function Slice (Stream : Stream_Type; Low : Positive; High : Natural) return String; private type Stream_Type is new Root_Stream_Type with record Position : Natural := 0; Buffer : XString; end record; end Test_Streams; gnatcoll-core-21.0.0/testsuite/tests/config/0000755000175000017500000000000013661715457020644 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/config/test_as.ini0000644000175000017500000000004713661715457023010 0ustar nicolasnicolasfile=test1.ini dir=.. bool=true int=15 gnatcoll-core-21.0.0/testsuite/tests/config/test2.ini0000644000175000017500000000001013661715457022375 0ustar nicolasnicolasinvalid gnatcoll-core-21.0.0/testsuite/tests/config/test1.ini0000644000175000017500000000032113661715457022401 0ustar nicolasnicolaskey1=value1 section1.key2 = value2_no_section int1 = 1 bool1=true bool2=False [section1] key2 = value2 key3 =value 3 # This is a comment = key4=value4 # This is another comment key[5=value5 [key6=value6 gnatcoll-core-21.0.0/testsuite/tests/config/test5.ini0000644000175000017500000000013213661715457022405 0ustar nicolasnicolas #comment=key key5_1 = value [section5_1] key5_1 = value2 [sec#tion] key5_2 = value3 gnatcoll-core-21.0.0/testsuite/tests/config/test.adb0000644000175000017500000001612613661715457022301 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Text_IO; with Ada.Directories; with GNATCOLL.Config; with Test_Assert; function Test return Integer is package IO renames Ada.Text_IO; package Dir renames Ada.Directories; package A renames Test_Assert; package Cfg renames GNATCOLL.Config; Ini : Cfg.INI_Parser; Ini2 : Cfg.INI_Parser; Ini3 : Cfg.INI_Parser; Sys : Cfg.INI_Parser; Pool : Cfg.Config_Pool; begin -- Open non existing file begin Ini.Open ("non_existent.ini"); A.Assert (False, "Name_Error should be raised"); exception when IO.Name_Error => A.Assert (True, "Name_Error raised if ini file does not exist"); when others => A.Assert (False, "Name_Error should have been raised"); end; -- Open a simple file IO.Put_Line ("Loading test1.ini"); Ini.Open ("test1.ini"); Pool.Fill (Ini); A.Assert (Pool.Get ("key1"), "value1", "check that key1=value1"); A.Assert (Pool.Get ("key2", Section => "section1"), "value2", "check that key2=value2 in section1"); A.Assert (Pool.Get ("key3", Section => "section1"), "value 3", "check that trailing spaces are ignored"); A.Assert (Pool.Get ("section1.key2", Section => Cfg.Section_From_Key), "value2", "check that sections1.key2=value2 (dot notation)"); A.Assert (Pool.Get ("section1.key4"), "value4", "check that comments around key " & "declaration do not have impacts"); A.Assert (Pool.Get ("section1.key[5"), "value5", "key can contain ["); A.Assert (Pool.Get ("section1.[key6"), "value6", "key can start with ["); -- Try to read invalid file (parser is very laxist and does not crash) Ini.Open ("test2.ini"); Pool.Fill (Ini); A.Assert (Pool.Get ("key1"), "value1", "check if key1=value1 is preserved"); A.Assert (Pool.Get ("invalid"), "", "check if invalid key exist"); -- Check if we can override values. -- The test reuse the same INI_Parser instance and thus check that on -- call to Open parser state is reset IO.Put_Line ("Loading test3.ini (parser reuse)"); Ini.Open ("test3.ini"); Pool.Fill (Ini); A.Assert (Pool.Get ("key1"), "value1_2", "check if key1 is overwritten"); A.Assert (Pool.Get ("key7"), "value7", "check addition of new key key7"); A.Assert (Pool.Get ("section1.key2"), "value2", "check if section1.key2=value2 is preserved"); -- Check if we can override values -- Use a new parser instance IO.Put_Line ("Loading test3.ini (new parser)"); Ini2.Open ("test3.ini"); Pool.Fill (Ini2); A.Assert (Pool.Get ("key1"), "value1_2", "check if key1 value is overwritten"); A.Assert (Pool.Get ("key7"), "value7", "check addtion of new key key7"); A.Assert (Pool.Get ("section1.key2"), "value2", "check if section1.key2=value2 is preserved"); -- Test non string values A.Assert (Pool.Get_Boolean ("bool1"), "boolean value (true)"); A.Assert (not Pool.Get_Boolean ("bool2"), "boolean value (false)"); A.Assert (Pool.Get_Integer ("int1") = 1, "integer value"); -- Test Config_Key creation declare CK1 : constant Cfg.Config_Key := Cfg.Create ("key1"); begin A.Assert (CK1.Get (Pool), "value1_2", "basic config_key test"); end; -- Check that introducing invalid gnatcoll templates strings do -- not crash the parser. IO.Put_Line ("Loading test4.ini (crashing the gnatcoll-template)"); begin Ini3.Open ("test4.ini"); Pool.Fill (Ini3); A.Assert (True, "parsing test4.ini"); exception when others => A.Assert (False, "parsing test4.ini"); end; -- Check some corner cases involving leading whitespaces for example IO.Put_Line ("Loading test5.ini"); begin Ini3.Open ("test5.ini"); Pool.Fill (Ini3); A.Assert (True, "parsing test5.ini"); A.Assert (Pool.Get ("#comment") /= "key", "ensure comment was parsed correctly"); A.Assert (Pool.Get ("key5_1") /= "value2", "ensure section was parsed correctly"); A.Assert (Pool.Get ("tion#key5_2", Section => "sec") /= "value3", "ensure # is not considered as a special character"); exception when others => A.Assert (False, "parsing test5.ini"); end; -- Check that a line containing only [ will not crash the parser begin Ini3.Open ("test6.ini"); Pool.Fill (Ini3); A.Assert (True, "parsing test6.ini"); exception when others => A.Assert (False, "parsing test6.ini"); end; -- Check system_id related functionality IO.Put_Line ("Loading test_sys.ini"); Sys.Set_System_Id (Dir.Current_Directory); Sys.Open ("test_as.ini"); A.Assert (Sys.As_Absolute_File, Dir.Full_Name ("test1.ini"), "check if file name is correctly retrieved from system ID"); Sys.Next; declare D : constant String := Sys.As_Absolute_Dir; begin A.Assert (D (D'First .. D'Last - 1), Dir.Full_Name (".."), -- strip sep "check if dir is correctly retrieved from system ID"); end; -- Check the rest of As_* functionality Sys.Next; A.Assert (Sys.As_Boolean, "check if boolean is correctly retrieved"); Sys.Next; A.Assert (Sys.As_Integer, 15, "check if integer is correctly retrieved"); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/config/test6.ini0000644000175000017500000000001413661715457022405 0ustar nicolasnicolas[ key=value gnatcoll-core-21.0.0/testsuite/tests/config/test3.ini0000644000175000017500000000003413661715457022404 0ustar nicolasnicolaskey1 = value1_2 key7=value7 gnatcoll-core-21.0.0/testsuite/tests/config/test.yaml0000644000175000017500000000007213661715457022506 0ustar nicolasnicolasdescription: Test for GNATCOLL.Config data: - "*.ini" gnatcoll-core-21.0.0/testsuite/tests/config/test4.ini0000644000175000017500000000007313661715457022410 0ustar nicolasnicolaskey8 = $e key9 = $: key10 = $( key11 = ${a =value12 key12= gnatcoll-core-21.0.0/testsuite/tests/storage_pools/0000755000175000017500000000000013661715457022257 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/storage_pools/headers_with_auxdec/0000755000175000017500000000000013743647711026254 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/storage_pools/headers_with_auxdec/test.adb0000644000175000017500000000220013661715457027677 0ustar nicolasnicolas-- Very simple test of GNATCOLL.Storage_Pools.Headers checking that it is -- useable together with DEC system extensions (S507-006). pragma Extend_System (Aux_DEC); with GNATCOLL.Storage_Pools.Headers; use GNATCOLL.Storage_Pools.Headers; with Ada.Text_IO; use Ada.Text_IO; with System.Address_Image; with Test_Assert; function Test return Integer is package A renames Test_Assert; type Header is record Refcount : Natural := 0; end record; type Header_Access is access all Header; package Pools is new Header_Pools (Header, Header_Access); package String_Pools is new Pools.Typed (String); Str : String_Pools.Element_Access; Hdr : Header_Access; begin -- Allocate element from the typed storage pool then retrieve -- and access its header Str := new String'("foo"); Put_Line (System.Address_Image (Str.all'Address)); Hdr := String_Pools.Header_Of (Str); Put_Line (System.Address_Image (Hdr.all'Address)); String_Pools.Header_Of (Str).all.Refcount := 1; A.Assert (Hdr.all.Refcount = 1, "unexpected ref count from extra header"); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/storage_pools/headers_with_auxdec/test.yaml0000644000175000017500000000022713743647711030120 0ustar nicolasnicolasdescription: Test GNATCOLL.Storage_Pools.Headers with Extend_System(Aux_DEC) control: - [XFAIL, "env.valgrind", "Known memory leak: see S912-005"] gnatcoll-core-21.0.0/testsuite/tests/strings/0000755000175000017500000000000013661715457021070 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/strings/basics/0000755000175000017500000000000013661715457022334 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/strings/basics/memory.ads0000644000175000017500000000333713661715457024343 0ustar nicolasnicolas------------------------------------------------------------------------------ -- Copyright (C) 2015-2016, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package Memory is Allocs : Integer := 0; -- Incremented every time we do a malloc Reallocs : Integer := 0; -- Incremented every time we do a realloc end Memory; gnatcoll-core-21.0.0/testsuite/tests/strings/basics/test.adb0000644000175000017500000014175313661715457023776 0ustar nicolasnicolaswith Ada.Command_Line; use Ada.Command_Line; with GNATCOLL.Asserts; with GNATCOLL.Strings; with GNATCOLL.Strings_Impl; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Wide_Characters.Handling; use Ada.Wide_Characters.Handling; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Text_IO; use Ada.Text_IO; with GNAT.Source_Info; with GNAT.Strings; use GNAT.Strings; with Memory; pragma Warnings (Off); with System.Memory; pragma Warnings (On); with Test_Assert; function Test return Integer is package A renames Test_Assert; generic with package Strings is new GNATCOLL.Strings_Impl.Strings (<>); with function Image (S : Strings.Char_String) return String is <>; First_Displayable : Strings.Char_Type := Strings.Char_Type'Val (Character'Pos ('A')); procedure Do_Test (Title : String); -- Tests for various instances of xstring type On_Error is new GNATCOLL.Asserts.Error_Reporter with null record; overriding procedure On_Assertion_Failed (Self : On_Error; Msg : String; Details : String; Location : String; Entity : String); Report : On_Error; package Asserts is new GNATCOLL.Asserts.Asserts (Report); package Equals_Boolean is new Asserts.Equals (Boolean, Boolean'Image); package Equals_Integer is new Asserts.Equals (Integer, Integer'Image); use Equals_Boolean, Equals_Integer; function Image (S : String) return String is ("--" & S & "--"); function Image (S : Wide_String) return String; -- For convenience of instantiation of Asserts, below. procedure Reset_Mem; -- Reset memory allocation counters --------------- -- Reset_Mem -- --------------- procedure Reset_Mem is begin Memory.Allocs := 0; Memory.Reallocs := 0; end Reset_Mem; ----------- -- Image -- ----------- function Image (S : Wide_String) return String is Result : Unbounded_String; begin Append (Result, "--"); for C of S loop if Wide_Character'Pos (C) <= 127 then Append (Result, Character'Val (Wide_Character'Pos (C))); else Append (Result, '[' & Integer'Image (Wide_Character'Pos (C)) & ']'); end if; end loop; Append (Result, "--"); return To_String (Result); end Image; ------------------------- -- On_Assertion_Failed -- ------------------------- overriding procedure On_Assertion_Failed (Self : On_Error; Msg : String; Details : String; Location : String; Entity : String) is pragma Unreferenced (Self); begin Put_Line ((if Msg = "" then "" else Msg & " ") & "(at " & Location & ", in " & Entity & ")" & ASCII.LF & " " & Details); end On_Assertion_Failed; ------------- -- Do_Test -- ------------- procedure Do_Test (Title : String) is use Strings; function Image (S : XString) return String is (Image (S.To_String)); package Equals1 is new Asserts.Equals (T => Strings.Char_Type, Image => Strings.Char_Type'Image); package Equals2 is new Asserts.Equals (T => Strings.Char_String, Image => Image); use Equals1, Equals2; generic type Left_Type (<>) is private; type Right_Type (<>) is private; Op : String; with function Compare (Left : Left_Type; Right : Right_Type) return Boolean; with function Image (L : Left_Type) return String is <>; with function Image (L : Right_Type) return String is <>; procedure Assert_Generic (Left : Left_Type; Right : Right_Type; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); procedure Assert_Generic (Left : Left_Type; Right : Right_Type; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if not Compare (Left, Right) then Asserts.Assert_Failed (Image (Left) & ' ' & Op & ' ' & Image (Right), Location => Location, Entity => Entity); end if; end Assert_Generic; procedure Assert is new Assert_Generic (XString, Char_String, "=", "="); procedure Assert is new Assert_Generic (Char_String, XString, "=", "="); procedure Assert is new Assert_Generic (XString, XString, "=", "="); procedure Assert_Less is new Assert_Generic (XString, Char_String, "<", "<"); procedure Assert_Less is new Assert_Generic (Char_String, XString, "<", "<"); procedure Assert_Less is new Assert_Generic (XString, XString, "<", "<"); procedure Assert_Less_Equal is new Assert_Generic (XString, Char_String, "<=", "<="); procedure Assert_Less_Equal is new Assert_Generic (Char_String, XString, "<=", "<="); procedure Assert_Less_Equal is new Assert_Generic (XString, XString, "<=", "<="); procedure Assert_Greater is new Assert_Generic (XString, Char_String, ">", ">"); procedure Assert_Greater is new Assert_Generic (Char_String, XString, ">", ">"); procedure Assert_Greater is new Assert_Generic (XString, XString, ">", ">"); procedure Assert_Greater_Equal is new Assert_Generic (XString, Char_String, ">=", ">="); procedure Assert_Greater_Equal is new Assert_Generic (Char_String, XString, ">=", ">="); procedure Assert_Greater_Equal is new Assert_Generic (XString, XString, ">=", ">="); Space : constant Char_Type := Char_Type'Val (Character'Pos (' ')); Spaces : constant Char_String := Space & Space; Newline : constant Char_Type := Char_Type'Val (Character'Pos (ASCII.LF)); Equal : constant Char_Type := Char_Type'Val (Character'Pos ('=')); Empty : Char_String (1 .. 0); Short : Char_String (3 .. 7); Long : Char_String (3 .. Integer (Strings.SSize'Last) + 10); Null_String : Char_String (1 .. 0); procedure Test_Append; procedure Test_Compare; procedure Test_Indexing (Base : Char_String); procedure Test_Trim; procedure Test_Substrings; procedure Test_Starts; procedure Test_Head_Tail; procedure Test_Iterate; procedure Test_Modify (Base : Char_String); procedure Test_Shrink; procedure Test_Swap; procedure Test_Justify (Base : Char_String); procedure Test_Split (Base : Char_String); procedure Test_Search (Base : Char_String); procedure Test_Casing; procedure Test_Access; ----------------- -- Test_Append -- ----------------- procedure Test_Append is S, S2 : XString; begin S.Set (Short); A.Assert (S = Short, Title); S.Set (Long); A.Assert (S = Long, Title); S2 := S; Append (S, Short); A.Assert (S2 = Long, Title); A.Assert (S = Long & Short, Title); S.Set (Short); A.Assert (S = Short, Title); A.Assert (S2 = Long, Title); -- Appending to a slice S.Set (Long); S2 := S; S.Slice (2, 3); S.Append (Short); A.Assert (S2 = Long, Title); A.Assert (S = Long (Long'First + 1 .. Long'First + 2) & Short, Title); end Test_Append; ------------------ -- Test_Compare -- ------------------ procedure Test_Compare is S, S2 : XString; begin -- Test equality between null strings S := Null_XString; if S /= Null_XString then raise Program_Error; end if; A.Assert (S.Is_Empty = True, Title); -- Test equality for short string and null string S := Null_XString; S.Set (Empty); A.Assert (S = Empty, Title); if S /= Null_XString then raise Program_Error; end if; Assert (Compare (S, Empty), 0); -- Test equality for big string and null string S := Null_XString; S.Set (Long); -- force malloc S.Set (Empty); -- reset to empty string A.Assert (S = Empty, Title); if S /= Null_XString then raise Program_Error; end if; A.Assert (S.Is_Empty = True, Title); A.Assert (Compare (S, Empty) = 0, Title); -- Test equality for short strings S := Null_XString; S2 := Null_XString; S.Set (Short); S2.Set (Short); if S /= S2 then raise Program_Error; end if; -- Test equality for long strings S := Null_XString; S2 := Null_XString; S.Set (Long); S2.Set (Long); if S /= S2 then raise Program_Error; end if; -- Test equality for short and long strings S := Null_XString; S2 := Null_XString; S.Set (Long); -- force malloc S.Set (Short); S2.Set (Short); if S /= S2 then raise Program_Error; end if; -- Test equality with Char_String declare XS : XString; S : Char_String := Short; begin XS.Set (Short); A.Assert (To_String(XS) = S, Title); A.Assert (To_XString(S) = XS, Title); A.Assert (Compare (XS, S) = 0, Title); end; -- Test equality with substrings declare S, S2, S3 : XString; begin S.Set (Short); S2.Set (Long); A.Assert (S = Short, Title); A.Assert (Short = S, Title); A.Assert (S2 = Long, Title); A.Assert (Long = S2, Title); A.Assert (S.Slice (2, 3) = S.Slice (2, 3), Title); A.Assert (Compare (S.Slice (2, 3), S.Slice (2, 3)) = 0, Title); A.Assert (S.Slice (2, 3) = S2.Slice (2, 3), Title); A.Assert (Compare (S.Slice (2, 3), S2.Slice (2, 3)) = 0, Title); A.Assert (S2.Slice (2, 3) = S.Slice (2, 3), Title); A.Assert (Compare (S2.Slice (2, 3), S.Slice (2, 3)) = 0, Title); A.Assert (S2.Slice (2, 3) = S2.Slice (2, 3), Title); A.Assert (Compare (S2.Slice (2, 3), S2.Slice (2, 3)) = 0, Title); -- Test inequality with substring A.Assert (Compare(S2,S) = 1, Title); A.Assert (Compare(S,S2) = -1, Title); -- Check that we have the same behavior as with standard strings A.Assert (S.Slice (3, 2) = Null_XString, Title); A.Assert (S.Slice (Natural'Last, 1) = Null_XString, Title); A.Assert (S.Slice (1, 0) = Null_XString, Title); S3 := S2; S3.Slice (3, 2); A.Assert (S3 = Null_XString, Title); S3 := S2; S3.Slice (Natural'Last, 1); A.Assert (S3 = Null_XString, Title); S3 := S2; S3.Slice (3, 0); A.Assert (S3 = Null_XString, Title); end; declare S, S2, S3 : XString; begin S.Set (Short); S2.Set (Long); Assert_Less (S, S2); Assert_Greater (S2, S); Assert_Less (S, Short (Short'First + 1 .. Short'Last)); Assert_Less_Equal (S, Short (Short'First + 1 .. Short'Last)); Assert_Greater_Equal (Short (Short'First + 1 .. Short'Last), S); Assert_Greater (Short (Short'First + 1 .. Short'Last), S); Assert_Less (Short, S.Slice (2, 3)); Assert_Less_Equal (Short, S.Slice (2, 3)); Assert_Greater_Equal (S.Slice (2, 3), Short); Assert_Greater (S.Slice (2, 3), Short); A.Assert (Compare (S, Short (Short'First + 1 .. Short'Last)) = -1, Title); A.Assert (Compare (Short (Short'First + 1 .. Short'Last), S) = 1, Title); Assert_Less (S2, Long (Long'First + 1 .. Long'Last)); Assert_Less (Long, S2.Slice (2, 3)); A.Assert (Compare (S2, Long (Long'First + 1 .. Long'Last)) = -1, Title); A.Assert (Compare (Long (Long'First + 1 .. Long'Last), S2) = 1, Title); A.Assert (Compare (S, S) = 0, Title); A.Assert (Compare (S2, S2) = 0, Title); S3.Set(Short(Short'First) & Char_Type'Val(Char_Type'Pos(Short(Short'First + 1)) + 1) & Short(Short'First + 2..Short'Last)); A.Assert (Compare (S3, S) = 1, Title); end; end Test_Compare; ------------------- -- Test_Indexing -- ------------------- procedure Test_Indexing (Base : Char_String) is Is_Long : constant Boolean := Base'Length > Natural (Strings.SSize'Last); S, S2 : XString; Idx : Positive; begin S.Set (Base); Reset_Mem; -- Test the constant indexing aspect -- We check memory allocations to make sure we are using -- a constant indexing aspect if S (1) /= First_Displayable then raise Program_Error; end if; if S (2) /= Char_Type'Succ (First_Displayable) then raise Program_Error; end if; A.Assert (Memory.Allocs = 0, Title); A.Assert (Memory.Reallocs = 0, Title); begin if S (10000) /= First_Displayable then null; end if; if S (Base'Length + 1) /= First_Displayable then null; end if; raise Program_Error; -- should have receive exeption exception when Ada.Strings.Index_Error => null; end; -- Testing loops -- We check memory allocations to make sure we are using -- a constant indexing aspect Reset_Mem; Idx := Base'First; for C of S loop Assert (C, Base (Idx)); Idx := Idx + 1; end loop; Assert (Idx, Base'Last + 1); S2 := S; -- Test that the string is still shareable A.Assert (S = Base, Title); A.Assert (S2 = Base, Title); A.Assert (Memory.Allocs = (if not Strings.Copy_On_Write and then Is_Long then 1 else 0), Title); A.Assert (Memory.Reallocs = 0, Title); -- Same test for loops on indexes Reset_Mem; Idx := Base'First; for Idx2 in S.Iterate loop A.Assert (Idx2 = Idx - Base'First + 1, Title); A.Assert (S (Idx2) = Base (Idx), Title); Idx := Idx + 1; end loop; Assert (Idx, Base'Last + 1); S2 := S; A.Assert (S = Base, Title); A.Assert (S2 = Base, Title); A.Assert (Memory.Allocs = (if not Strings.Copy_On_Write and then Is_Long then 1 else 0), Title); A.Assert (Memory.Reallocs = 0, Title); -- Test the Variable indexing aspect S.Set (Base); S (2) := First_Displayable; A.Assert (S = Base (Base'First) & First_Displayable & Base (Base'First + 2 .. Base'Last), Title); Reset_Mem; S.Set (Base); S2 := S; S (2) := First_Displayable; -- makes a copy A.Assert (S2 = Base, Title); A.Assert (S = Base (Base'First) & First_Displayable & Base (Base'First + 2 .. Base'Last), Title); A.Assert (Memory.Allocs = (if Is_Long then 1 else 0), Title); A.Assert (Memory.Reallocs = 0, Title); Reset_Mem; S.Set (Base); declare R : Character_Reference := S.Reference (2); begin S2 := S; R := First_Displayable; A.Assert (S2 = Base, Title); A.Assert (S = Base (Base'First) & First_Displayable & Base (Base'First + 2 .. Base'Last), Title); end; A.Assert (Memory.Allocs = (if Is_Long then 1 else 0), Title); A.Assert (Memory.Reallocs = 0, Title); end Test_Indexing; --------------- -- Test_Trim -- --------------- procedure Test_Trim is pragma Compile_Time_Error (Short'Length + 2 * Spaces'Length > Integer (SSize'Last), "Cannot test short string with spaces"); -- So that we can test Trim with small strings. S, S2 : XString; begin -- Short strings S.Set (Spaces & Short & Spaces); A.Assert (S.Trim (Chars => Space) = Short, Title); A.Assert (S.Trim (Ada.Strings.Right) = Spaces & Short, Title); A.Assert (S.Trim (Ada.Strings.Left) = Short & Spaces, Title); S.Trim (Chars => Space); A.Assert (S = Short, Title); S.Set (Spaces & Short & Spaces); S.Trim (Side => Ada.Strings.Right, Chars => Space); A.Assert (S = Spaces & Short, Title); S.Set (Spaces & Short & Spaces); S.Trim (Side => Ada.Strings.Left, Chars => Space); A.Assert (S = Short & Spaces, Title); -- Long strings S.Set (Spaces & Long & Spaces); A.Assert (S.Trim = Long, Title); A.Assert (S.Trim (Ada.Strings.Right) = Spaces & Long, Title); A.Assert (S.Trim (Ada.Strings.Left) = Long & Spaces, Title); S2 := S; S.Trim (Chars => Space); A.Assert (S = Long, Title); S := S2; S.Trim (Side => Ada.Strings.Right, Chars => Space); A.Assert (S = Spaces & Long, Title); S := S2; S.Trim (Side => Ada.Strings.Left, Chars => Space); A.Assert (S = Long & Spaces, Title); -- Long strings with First /= 1 S.Set (Spaces & Spaces & Long & Spaces & Spaces); S.Slice (3, Spaces'Length * 4 + Long'Length - 2); A.Assert (S.Trim = Long, Title); A.Assert (S.Trim (Ada.Strings.Right) = Spaces & Long, Title); A.Assert (S.Trim (Ada.Strings.Left) = Long & Spaces, Title); S2 := S; S.Trim; A.Assert (S = Long, Title); S := S2; S.Trim (Ada.Strings.Right); A.Assert (S = Spaces & Long, Title); S := S2; S.Trim (Ada.Strings.Left); A.Assert (S = Long & Spaces, Title); end Test_Trim; --------------------- -- Test_Substrings -- --------------------- procedure Test_Substrings is S, S2, S3 : XString; begin -- Short strings S.Set (Spaces & Short & Spaces); S2 := S; -- shared buffer S.Trim; -- get a substring S2.Append (Short); -- no longer shared S3 := S; -- shared buffer S3.Append (Short); -- no longer shared A.Assert (S = Short, Title); A.Assert (S2 = Spaces & Short & Spaces & Short, Title); A.Assert (S3 = Short & Short, Title); -- Check that first character is still index 1 even with -- small strings. A.Assert (S (1) = First_Displayable, Title); end Test_Substrings; ----------------- -- Test_Starts -- ----------------- procedure Test_Starts is S : XString; begin S.Set (Short); A.Assert (S.Starts_With (Short) = True, Title); A.Assert (S.Starts_With (Short (3 .. 4)) = True, Title); A.Assert (S.Starts_With (Null_XString) = True, Title); A.Assert (S.Starts_With (Null_String) = True, Title); A.Assert (S.Starts_With (Short & First_Displayable) = False, Title); A.Assert (S.Ends_With (Short) = True, Title); A.Assert (S.Ends_With (Short (Short'Last - 1 .. Short'Last)) = True, Title); A.Assert (S.Ends_With (Null_XString) = True, Title); A.Assert (S.Ends_With (Null_String) = True, Title); A.Assert (S.Ends_With (Short & First_Displayable) = False, Title); S.Set (Long); A.Assert (S.Starts_With (Long) = True, Title); A.Assert (S.Starts_With (Long (3 .. 4)) = True, Title); A.Assert (S.Starts_With (Null_XString) = True, Title); A.Assert (S.Starts_With (Null_String) = True, Title); A.Assert (S.Starts_With (Long & First_Displayable) = False, Title); A.Assert (S.Ends_With (Long) = True, Title); A.Assert (S.Ends_With (Long (Long'Last - 1 .. Long'Last)) = True, Title); A.Assert (S.Ends_With (Null_XString) = True, Title); A.Assert (S.Ends_With (Null_String) = True, Title); A.Assert (S.Ends_With (Long & First_Displayable) = False, Title); end Test_Starts; -------------------- -- Test_Head_Tail -- -------------------- procedure Test_Head_Tail is S, S2 : XString; begin S.Set (Short); A.Assert (S.Head (1) = Short (Short'First .. Short'First), Title); A.Assert (S.Head (2) = Short (Short'First .. Short'First + 1), Title); A.Assert (S.Head (1000) = Short, Title); A.Assert (S.Tail (1) = Short (Short'Last .. Short'Last), Title); A.Assert (S.Tail (2) = Short (Short'Last - 1 .. Short'Last), Title); A.Assert (S.Tail (1000) = Short, Title); S.Set (Long); A.Assert (S.Head (1) = Long (Long'First .. Long'First), Title); A.Assert (S.Head (2) = Long (Long'First .. Long'First + 1), Title); A.Assert (S.Head (1000) = Long, Title); A.Assert (S.Tail (1) = Long (Long'Last .. Long'Last), Title); A.Assert (S.Tail (2) = Long (Long'Last - 1 .. Long'Last), Title); A.Assert (S.Tail (1000) = Long, Title); S.Set (Long); S.Slice (3, 10); A.Assert (S.Head (1) = Long (Long'First + 2 .. Long'First + 2), Title); A.Assert (S.Head (2) = Long (Long'First + 2 .. Long'First + 3), Title); A.Assert (S.Head (1000) = Long (Long'First + 2 .. Long'First + 9), Title); A.Assert (S.Tail (1) = Long (Long'First + 9 .. Long'First + 9), Title); A.Assert (S.Tail (2) = Long (Long'First + 8 .. Long'First + 9), Title); A.Assert (S.Tail (1000) =Long (Long'First + 2 .. Long'First + 9), Title); -- A slice into itself S.Set (Long); S.Slice (3, 10, Into => S); A.Assert (S = Long (Long'First + 2 .. Long'First + 9), Title); -- A slice into an already allocated string should not leak -- memory. S.Set (Long); S2.Set (Long); S.Slice (3, 10, Into => S2); A.Assert (S = Long, Title); A.Assert (S2 = Long (Long'First + 2 .. Long'First + 9), Title); end Test_Head_Tail; ------------------ -- Test_Iterate -- ------------------ procedure Test_Iterate is S : XString; Expected : Natural; E : Char_Type; begin -- Iterate on indexes, short string S.Set (Short); Expected := 1; for Index of S.Iterate loop A.Assert (Index = Expected, Title); Expected := Expected + 1; end loop; A.Assert (Expected = Short'Length + 1, Title); Expected := 1; for Index of S.Slice (2, 3).Iterate loop A.Assert (Index = Expected, Title); Expected := Expected + 1; end loop; A.Assert (Expected = 3, Title); -- Iterate on characters, short string E := First_Displayable; for C of S loop A.Assert (C = E, Title); E := Char_Type'Succ (E); end loop; A.Assert (Char_Type'Pos (E) = Char_Type'Pos (First_Displayable) + Short'Length, Title); E := Char_Type'Succ (First_Displayable); for C of S.Slice (2, 3) loop A.Assert (C = E, Title); E := Char_Type'Succ (E); end loop; A.Assert (Char_Type'Pos (E) = Char_Type'Pos (First_Displayable) + 3, Title); -- Iterate on indexes, long string S.Set (Long); Expected := 1; for Index of S.Iterate loop A.Assert (Index = Expected, Title); Expected := Expected + 1; end loop; A.Assert (Expected = Long'Length + 1, Title); Expected := 1; for Index of S.Slice (2, 10).Iterate loop A.Assert (Index = Expected, Title); Expected := Expected + 1; end loop; A.Assert (Expected = 10, Title); -- Iterate on characters, long string E := First_Displayable; for C of S loop A.Assert (C = E, Title); E := Char_Type'Succ (E); end loop; A.Assert (Char_Type'Pos (E) = Char_Type'Pos (First_Displayable) + Long'Length, Title); E := Char_Type'Succ (First_Displayable); for C of S.Slice (2, 10) loop A.Assert (C = E, Title); E := Char_Type'Succ (E); end loop; A.Assert (Char_Type'Pos (E) = Char_Type'Pos (First_Displayable) + 10, Title); end Test_Iterate; ----------------- -- Test_Modify -- ----------------- procedure Test_Modify(Base : Char_String) is S, S2 : XString; begin S.Set (Base); S2 := S; S.Replace (2, First_Displayable); A.Assert (S = Base (Base'First) & First_Displayable & Base (Base'First + 2 .. Base'Last), Title); A.Assert (S2 = Base, Title); -- Replace with small string S.Set (Base); S2 := S; S.Replace (2, 3, (3 => First_Displayable)); A.Assert (S = Base (Base'First) & First_Displayable & Base (Base'First + 3 .. Base'Last), Title); A.Assert (S2 = Base, Title); -- Replace with longer string S.Set (Base); S2 := S; S.Replace (2, 3, (3 .. 20 => First_Displayable)); A.Assert (S = Base (Base'First) & (3 .. 20 => First_Displayable) & Base (Base'First + 3 .. Base'Last), Title); A.Assert (S2 = Base, Title); -- Replace an empty string (same as insert) S.Set (Base); S2 := S; S.Replace (2, 1, (3 .. 20 => First_Displayable)); A.Assert (S = Base (Base'First) & (3 .. 20 => First_Displayable) & Base (Base'First + 1 .. Base'Last), Title); A.Assert (S2 = Base, Title); -- Replace with self S.Set (Base); S.Replace_Slice (1, 1, S); A.Assert (S = Base & Base (Base'First + 1 .. Base'Last), Title); S.Set (Base); S2 := S; S.Insert (3, First_Displayable & First_Displayable); A.Assert (S = Base (Base'First .. Base'First + 1) & First_Displayable & First_Displayable & Base (Base'First + 2 .. Base'Last), Title); A.Assert (S2 = Base, Title); -- Overwrite with shorter string S.Set (Base); S2 := S; S.Overwrite (3, Base (Base'First .. Base'First + 1)); A.Assert (S = Base (Base'First .. Base'First + 1) & Base (Base'First .. Base'First + 1) & Base (Base'First + 4 .. Base'Last), Title); A.Assert (S2 = Base, Title); -- Overwrite with longer string S.Set (Base); S2 := S; S.Overwrite (3, Base); A.Assert (S = Base (Base'First .. Base'First + 1) & Base, Title); A.Assert (S2 = Base, Title); -- Deleting S.Set (Base); S2 := S; S.Delete (3, 4); A.Assert (S = Base (Base'First .. Base'First + 1) & Base (Base'First + 4 .. Base'Last), Title); A.Assert (S2 = Base, Title); -- Deleting past the end S.Set (Base); S2 := S; S.Delete (3, 10_000); A.Assert (S = Base (Base'First .. Base'First + 1), Title); A.Assert (S2 = Base, Title); -- Clearing the string S.Set (Base); S2 := S; S.Clear; A.Assert (S2 = Base, Title); A.Assert (S = Null_String, Title); end Test_Modify; ----------------- -- Test_Shrink -- ----------------- procedure Test_Shrink is S : XString; begin S.Set (Short); S.Shrink; A.Assert (S = Short, Title); S := 50 * Long; S.Set (Long); S.Shrink; A.Assert (S = Long, Title); S.Set (Short); S.Shrink; A.Assert (S = Short, Title); end Test_Shrink; --------------- -- Test_Swap -- --------------- procedure Test_Swap is S, S2 : XString; begin -- Swapping two short strings S.Set (Short); S2.Set (Spaces); S.Swap (S2); A.Assert (S = Spaces, Title); A.Assert (S2 = Short, Title); S.Swap (S2); A.Assert (S = Short, Title); A.Assert (S2 = Spaces, Title); -- Swapping a short and a long string S.Set (Short); S2.Set (Long); S.Swap (S2); A.Assert (S = Long, Title); A.Assert (S2 = Short, Title); S.Swap (S2); A.Assert (S = Short, Title); A.Assert (S2 = Long, Title); -- Swapping self S.Set (Short); S.Swap (S); A.Assert (S = Short, Title); S.Set (Long); S.Swap (S); A.Assert (S = Long, Title); end Test_Swap; ------------------ -- Test_Justify -- ------------------ procedure Test_Justify (Base : Char_String) is S, S2 : XString; begin -- procedure Center S.Set (Base); S.Center (Width => Base'Length + 4); A.Assert (S = Space & Space & Base & Space & Space, Title); S.Clear; S.Set (Base); S.Center (Width => Base'Length + 3); A.Assert (S = Space & Space & Base & Space, Title); S.Clear; S.Set (Base); S.Center (Width => Base'Length - 1); A.Assert (S = Base, Title); S.Clear; S.Set (Base); S.Center (Base'Length + 2, First_Displayable); A.Assert (S = First_Displayable & Base & First_Displayable, Title); -- function Center S.Clear; S.Set (Base); A.Assert (S.Center (Base'Length + 4) = Space & Space & Base & Space & Space, Title); A.Assert (S.Center (Base'Length + 3) = Space & Space & Base & Space, Title); A.Assert (S.Center (Base'Length - 1) = Base, Title); -- procedure Left justify S.Clear; S.Set (Base); S.Left_Justify (Width => Base'Length + 2); A.Assert (S = Base & Space & Space, Title); S.Clear; S.Set (Base); S.Left_Justify (Width => Base'Length - 1); A.Assert (S = Base, Title); S.Clear; S.Set (Base); S.Left_Justify (Base'Length + 2, First_Displayable); A.Assert (S = Base & First_Displayable & First_Displayable, Title); -- function Left justify S.Clear; S.Set (Base); A.Assert (S.Left_Justify (Base'Length + 2) = Base & Space & Space, Title); A.Assert (S.Left_Justify (Base'Length - 1) = Base, Title); A.Assert (S.Left_Justify (Base'Length + 2, First_Displayable) = Base & First_Displayable & First_Displayable, Title); -- procedure Right justify S.Clear; S.Set (Base); S.Right_Justify (Width => Base'Length + 2); A.Assert (S = Space & Space & Base, Title); S.Clear; S.Set (Base); S.Right_Justify (Width => Base'Length - 1); A.Assert (S = Base, Title); S.Clear; S.Set (Base); S.Right_Justify (Base'Length + 2, First_Displayable); A.Assert (S = First_Displayable & First_Displayable & Base, Title); -- function Right justify S.Set (Base); A.Assert (S.Right_Justify (Base'Length + 2) = Space & Space & Base, Title); A.Assert (S.Right_Justify (Base'Length - 1) = Base, Title); A.Assert (S.Right_Justify (Base'Length + 2, First_Displayable) = First_Displayable & First_Displayable & Base, Title); end Test_Justify; ---------------- -- Test_Split -- ---------------- procedure Test_Split (Base : Char_String) is Is_Long : constant Boolean := Base'Length > Natural (Strings.SSize'Last); S, S2 : XString; begin S.Set (Space & Space & Base & Space & Base & Space & Base & Space & Space); Reset_Mem; declare R : constant XString_Array := S.Split (Space); begin -- 3 copies per non-null substring when not copy-on-write -- 1 copy when creating the slide -- 2 copies when returning the array, since have a temporary -- created by the compiler A.Assert (Memory.Allocs = (if Is_Long and not Copy_On_Write then 9 else 0), Title); A.Assert (Memory.Reallocs = 0, Title); A.Assert (R'Length = 7, Title); A.Assert (R (R'First + 0) = Null_XString, Title); A.Assert (R (R'First + 1) = Null_String, Title); A.Assert (R (R'First + 2) = Base, Title); A.Assert (R (R'First + 3) = Base, Title); A.Assert (R (R'First + 4) = Base, Title); A.Assert (R (R'First + 5) = Null_String, Title); A.Assert (R (R'First + 6) = Null_String, Title); S2.Set_As_Join (Space, R); A.Assert (S2 = S, Title); A.Assert (Join (Space, R) = S, Title); A.Assert (Join ((1 => Space), R) = S, Title); end; Reset_Mem; declare R : XString_Array (1 .. 20); Last : Natural; begin S.Split (Space, Into => R, Last => Last); -- One copy per non-null substring when not copy-on-write A.Assert (Memory.Allocs = (if Is_Long and not Copy_On_Write then 3 else 0), Title); A.Assert (Memory.Reallocs = 0, Title); A.Assert (Last = 7, Title); end; Reset_Mem; declare R : constant XString_Array := S.Right_Split (Space); begin -- Three copies per non-null substring when not copy-on-write A.Assert (Memory.Allocs = (if Is_Long and not Copy_On_Write then 9 else 0), Title); A.Assert (Memory.Reallocs = 0, Title); A.Assert (R'Length = 7, Title); A.Assert (R (R'First + 6) = Null_XString, Title); A.Assert (R (R'First + 5) = Null_String, Title); A.Assert (R (R'First + 4) = Base, Title); A.Assert (R (R'First + 3) = Base, Title); A.Assert (R (R'First + 2) = Base, Title); A.Assert (R (R'First + 1) = Null_String, Title); A.Assert (R (R'First + 0) = Null_String, Title); end; Reset_Mem; declare R : XString_Array (1 .. 20); Last : Natural; begin S.Right_Split (Space, Into => R, Last => Last); A.Assert (Memory.Allocs = (if Is_Long and not Copy_On_Write then 3 else 0), Title); A.Assert (Memory.Reallocs = 0, Title); A.Assert (Last = 7, Title); end; declare R : constant XString_Array := S.Split (Space, Max_Split => 1); R2 : constant XString_Array := S.Right_Split (Space, Max_Split => 1); begin A.Assert (R'Length = 1, Title); A.Assert (R (R'First + 0) = S, Title); A.Assert (R2'Length = 1, Title); A.Assert (R2 (R2'First + 0) = S, Title); S2.Set_As_Join (Space, R); A.Assert (S2 = S, Title); A.Assert (Join (Space, R) = S, Title); A.Assert (Join ((1 => Space), R) = S, Title); end; declare R : constant XString_Array := S.Split (Space, Max_Split => 2); R2 : constant XString_Array := S.Right_Split (Space, Max_Split => 2); begin A.Assert (R'Length = 2, Title); A.Assert (R (R'First + 0) = Null_XString, Title); A.Assert (R (R'First + 1) = Space & Base & Space & Base & Space & Base & Space & Space, Title); A.Assert (R2'Length = 2, Title); A.Assert (R2 (R2'First + 0) = Null_XString, Title); A.Assert (R2 (R2'First + 1) = Space & Space & Base & Space & Base & Space & Base & Space, Title); S2.Set_As_Join (Space, R); A.Assert (S2 = S, Title); A.Assert (Join (Space, R) = S, Title); A.Assert (Join ((1 => Space), R) = S, Title); end; declare R : constant XString_Array := S.Split (Space, Omit_Empty => True); R2 : constant XString_Array := S.Right_Split (Space, Omit_Empty => True); begin A.Assert (R'Length = 3, Title); A.Assert (R (R'First + 0) = Base, Title); A.Assert (R (R'First + 1) = Base, Title); A.Assert (R (R'First + 2) = Base, Title); A.Assert (R2'Length = 3, Title); A.Assert (R2 (R2'First + 2) = Base, Title); A.Assert (R2 (R2'First + 1) = Base, Title); A.Assert (R2 (R2'First + 0) = Base, Title); end; declare R : constant XString_Array := Null_XString.Split (Space, Omit_Empty => True); R2 : constant XString_Array := Null_XString.Right_Split (Space, Omit_Empty => True); begin A.Assert (R'Length = 0, Title); A.Assert (R2'Length = 0, Title); end; -- Splitting on strings declare R : constant XString_Array := S.Split (Space & Space); begin A.Assert (R'Length = 3, Title); A.Assert (R (R'First + 0) = Null_XString, Title); A.Assert (R (R'First + 1) = Base & Space & Base & Space & Base, Title); A.Assert (R (R'First + 2) = Null_XString, Title); end; declare R : constant XString_Array := S.Split (Space & Space, Omit_Empty => True); begin A.Assert (R'Length = 1, Title); A.Assert (R (R'First + 0) = Base & Space & Base & Space & Base, Title); end; A.Assert (Null_XString.Split (Space & Space)'Length = 0, Title); A.Assert (Null_XString.Split (Null_String)'Length = 0, Title); A.Assert (S.Split (Null_String)'Length = 0, Title); declare R : constant XString_Array := S.Right_Split (Space & Space); begin A.Assert (R'Length = 3, Title); A.Assert (R (R'First + 0) = Null_XString, Title); A.Assert (R (R'First + 1) = Base & Space & Base & Space & Base, Title); A.Assert (R (R'First + 2) = Null_XString, Title); end; declare R : constant XString_Array := S.Right_Split (Space & Space, Omit_Empty => True); begin A.Assert (R'Length = 1, Title); A.Assert (R (R'First + 0) = Base & Space & Base & Space & Base, Title); end; A.Assert (Null_XString.Right_Split (Space & Space)'Length = 0, Title); A.Assert (Null_XString.Right_Split (Null_String)'Length = 0, Title); A.Assert (S.Right_Split (Null_String)'Length = 0, Title); -- Joining A.Assert (Join (Null_XString, XString_Array'(1 .. 0 => Null_XString)) = Null_XString, Title); A.Assert (Join (Space, XString_Array'(1 .. 0 => Null_XString)) = Null_XString, Title); A.Assert (Join (Space & Space, XString_Array'(1 .. 0 => Null_XString)) = Null_XString, Title); -- Parsing a file containing "key = value" lines declare L : XString_Array (1 .. 2); Key, Value : XString; Last : Natural; begin S.Set (Spaces & Newline & Base & Equal & Base & Newline & Base & Equal & Base & Newline & Base & Equal & Base & Newline); Reset_Mem; for Line of S.Split (Newline) loop Line.Split (Equal, Into => L, Last => Last); if Last = 2 then Key := L (1); Key.Trim; Value := L (2); Value.Trim; end if; end loop; A.Assert (Memory.Allocs = (if Is_Long and not Copy_On_Write then 14 else 0), Title); A.Assert (Memory.Reallocs = 0, Title); end; end Test_Split; ----------------- -- Test_Search -- ----------------- procedure Test_Search (Base : Char_String) is S, S2 : XString; begin S.Set (Space & Space & Base & Space & Base & Space & Base & Space & Space); A.Assert (S.Count (Space) = 6, Title); A.Assert (S.Count (Char_Type'Val (0)) = 0, Title); A.Assert (S.Count (Space & Space) = 2, Title); A.Assert (S.Count (First_Displayable & Char_Type'Succ (First_Displayable)) = 3, Title); A.Assert (S.Count (Null_String) = Natural'Last, Title); A.Assert (Null_XString.Count (Space) = 0, Title); A.Assert (Null_XString.Count (Space & Space) = 0, Title); A.Assert (Null_XString.Count (Null_String) = 0, Title); A.Assert (S.Find (Space) = 1, Title); A.Assert (S.Find (Space & Space) = 1, Title); A.Assert (S.Slice (2, 3).Find (Space) = 1, Title); A.Assert (S.Slice (3, Base'Length * 2).Find (Space) = Base'Length + 1, Title); A.Assert (S.Find (Space, Low => 2, High => 3) = 2, Title); A.Assert (S.Find (Space, Low => 3) = Base'Length + 3, Title); A.Assert (S.Find (Base, Low => 3, High => 4) = 0, Title); A.Assert (S.Find (First_Displayable) = 3, Title); A.Assert (S.Find (Base) = 3, Title); A.Assert (S.Right_Find (Space) = S.Length, Title); A.Assert (S.Right_Find (Space & Space) = S.Length - 1, Title); A.Assert (S.Right_Find (Base) = S.Length - 1 - Base'Length, Title); A.Assert (S.Find (First_Displayable & First_Displayable) = 0, Title); A.Assert (S.Right_Find (First_Displayable & First_Displayable) = 0, Title); end Test_Search; ----------------- -- Test_Casing -- ----------------- procedure Test_Casing is S : XString; S2 : XString; begin S.Set (Short); A.Assert (S.Is_Upper = True, Title); A.Assert (S.Is_Lower = False, Title); S2 := To_Lower(S); A.Assert (S2.Is_Upper = False, Title); A.Assert (S2.Is_Lower = True, Title); S.To_Lower; A.Assert (S.Is_Upper = False, Title); A.Assert (S.Is_Lower = True, Title); S.Capitalize; A.Assert ((Slice (S, 1, 1).Is_Upper and Slice (S, 2, S.Length).Is_Lower) = True, Title); A.Assert ((Slice (S, 1, 1).Is_Lower or Slice (S, 2, S.Length).Is_Upper) = False, Title); S2 := To_Upper(S); A.Assert (S2.Is_Upper = True, Title); A.Assert (S2.Is_Lower = False, Title); S.To_Upper; A.Assert (S.Is_Upper = True, Title); A.Assert (S.Is_Lower = False, Title); S.Set (Long); A.Assert (S.Is_Upper = (Long'Length < 100), Title); A.Assert (S.Is_Lower = False, Title); S2 := To_Lower(S); A.Assert (S2.Is_Upper = False, Title); A.Assert (S2.Is_Lower = True, Title); S.To_Lower; A.Assert (S.Is_Upper = False, Title); A.Assert (S.Is_Lower = True, Title); S2 := To_Upper(S); A.Assert (S2.Is_Upper = True, Title); A.Assert (S2.Is_Lower = False, Title); S.To_Upper; A.Assert (S.Is_Upper = True, Title); A.Assert (S.Is_Lower = False, Title); S.Capitalize; A.Assert ((Slice (S, 1, 1).Is_Upper and Slice (S, 2, S.Length).Is_Lower) = True, Title); A.Assert ((Slice (S, 1, 1).Is_Lower or Slice (S, 2, S.Length).Is_Upper) = False, Title); end Test_Casing; ----------------- -- Test_Access -- ----------------- procedure Test_Access is S : XString; Is_Long : Boolean; procedure Callback (Data : Char_String) is begin A.Assert (Data = (if Is_Long then Long else Short), Title); S.Append (Space); A.Assert (Data = (if Is_Long then Long else Short), Title); A.Assert (S = (if Is_Long then Long else Short) & Space, Title); S := Null_XString; A.Assert (Data = (if Is_Long then Long else Short), Title); end Callback; begin S.Set (Short); Is_Long := False; S.Access_String (Callback'Access); S.Set (Long); Is_Long := True; S.Access_String (Callback'Access); end Test_Access; begin declare C : Char_Type := First_Displayable; begin for S in Short'Range loop Short (S) := C; C := Char_Type'Succ (C); end loop; C := First_Displayable; for S in Long'Range loop Long (S) := C; C := Char_Type'Succ (C); end loop; end; declare -- coverage of function '*' (Count : Natural; -- Right : Char_Type) return XString -- and of function '*' (Count : Natural; -- Right : XString) return XString S1 : XString := 4 * First_Displayable; S2 : XString := 4 * S1; begin A.Assert (S1.Length = 4); A.Assert (S1 (1) = First_Displayable); A.Assert (S1 (S1.Length) = First_Displayable); A.Assert (S2.Length = 16); A.Assert (S2 (1) = First_Displayable); A.Assert (S2 (S2.Length) = First_Displayable); end; Test_Append; Test_Compare; Test_Indexing (Short); Test_Indexing (Long); Test_Trim; Test_Substrings; Test_Starts; Test_Head_Tail; Test_Iterate; Test_Modify (Short); Test_Modify (Long); Test_Shrink; Test_Swap; Test_Justify (Short); Test_Justify (Long); Test_Search (Short); Test_Search (Long); Test_Split (Short); Test_Split (Long); Test_Casing; Test_Access; end Do_Test; procedure Test_COW is new Do_Test (GNATCOLL.Strings); package Basic_No_COW is new GNATCOLL.Strings_Impl.Strings (GNATCOLL.Strings_Impl.Optimal_String_Size, Character, String, Copy_On_Write => False); procedure Test_No_COW is new Do_Test (Basic_No_COW); type SSize_3 is mod 128; package Strings3 is new GNATCOLL.Strings_Impl.Strings (SSize_3, Character, String); procedure Test3 is new Do_Test (Strings3); type SSize_2 is mod 10; package Wide is new GNATCOLL.Strings_Impl.Strings (SSize_2, Wide_Character, Wide_String, Copy_On_Write => True); procedure Test_Wide is new Do_Test (Wide); begin Test_No_COW("basic strings, no COW"); Test_COW("basic strings, with COW"); Test3("strings with size" & SSize_3'Last'Img); Test_Wide("wide strings"); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/strings/basics/test.yaml0000644000175000017500000000004713661715457024200 0ustar nicolasnicolasdescription: Test for GNATCOLL.Strings gnatcoll-core-21.0.0/testsuite/tests/strings/basics/test.gpr0000644000175000017500000000062013661715457024023 0ustar nicolasnicolaswith "gnatcoll"; project Test is for Main use ("test.adb"); for Object_Dir use "obj"; for Source_Dirs use (".", "../../../support"); package Compiler is for Switches ("Ada") use ("-g", "-gnateE"); for Switches ("s-memory.adb") use ("-gnatg") & Compiler'Switches ("Ada"); end Compiler; package Binder is for Switches ("Ada") use ("-E"); end Binder; end Test; gnatcoll-core-21.0.0/testsuite/tests/strings/basics/s-memory.adb0000644000175000017500000001221013661715457024550 0ustar nicolasnicolas------------------------------------------------------------------------------ -- -- -- GNAT RUN-TIME COMPONENTS -- -- -- -- S Y S T E M . M E M O R Y -- -- -- -- B o d y -- -- -- -- Copyright (C) 2001-2016, Free Software Foundation, Inc. -- -- -- -- GNAT is free software; you can redistribute it and/or modify it under -- -- terms of the GNU General Public License as published by the Free Soft- -- -- ware Foundation; either version 3, or (at your option) any later ver- -- -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- -- or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ -- Copied from System.Memory, but counts the number of times we do -- malloc and realloc. pragma Compiler_Unit_Warning; with Ada.Exceptions; with System.Soft_Links; with System.Parameters; with System.CRTL; with Memory; package body System.Memory is use Ada.Exceptions; use System.Soft_Links; function c_malloc (Size : System.CRTL.size_t) return System.Address renames System.CRTL.malloc; procedure c_free (Ptr : System.Address) renames System.CRTL.free; function c_realloc (Ptr : System.Address; Size : System.CRTL.size_t) return System.Address renames System.CRTL.realloc; ----------- -- Alloc -- ----------- function Alloc (Size : size_t) return System.Address is Result : System.Address; Actual_Size : size_t := Size; begin if Size = size_t'Last then Raise_Exception (Storage_Error'Identity, "object too large"); end if; -- Change size from zero to non-zero. We still want a proper pointer -- for the zero case because pointers to zero length objects have to -- be distinct, but we can't just go ahead and allocate zero bytes, -- since some malloc's return zero for a zero argument. if Size = 0 then Actual_Size := 1; end if; if Parameters.No_Abort then Result := c_malloc (System.CRTL.size_t (Actual_Size)); else Abort_Defer.all; Result := c_malloc (System.CRTL.size_t (Actual_Size)); Abort_Undefer.all; end if; Standard.Memory.Allocs := Standard.Memory.Allocs + 1; if Result = System.Null_Address then Raise_Exception (Storage_Error'Identity, "heap exhausted"); end if; return Result; end Alloc; ---------- -- Free -- ---------- procedure Free (Ptr : System.Address) is begin if Parameters.No_Abort then c_free (Ptr); else Abort_Defer.all; c_free (Ptr); Abort_Undefer.all; end if; end Free; ------------- -- Realloc -- ------------- function Realloc (Ptr : System.Address; Size : size_t) return System.Address is Result : System.Address; Actual_Size : constant size_t := Size; begin if Size = size_t'Last then Raise_Exception (Storage_Error'Identity, "object too large"); end if; if Parameters.No_Abort then Result := c_realloc (Ptr, System.CRTL.size_t (Actual_Size)); else Abort_Defer.all; Result := c_realloc (Ptr, System.CRTL.size_t (Actual_Size)); Abort_Undefer.all; end if; if Result = System.Null_Address then Raise_Exception (Storage_Error'Identity, "heap exhausted"); end if; Standard.Memory.Reallocs := Standard.Memory.Reallocs + 1; return Result; end Realloc; end System.Memory; gnatcoll-core-21.0.0/testsuite/tests/pools/0000755000175000017500000000000013661715457020533 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/pools/test.adb0000644000175000017500000000614613661715457022171 0ustar nicolasnicolas------------------------------------------------------------------------------ -- -- -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Pools; with GNATCOLL.Traces; with Test_Assert; function Test return Integer is package A renames Test_Assert; function Factory (Param : Integer) return Integer; -- Simple integer factory type Resource_Set is new Integer range 1 .. 1; package Int_Pools is new GNATCOLL.Pools (Element_Type => Integer, Factory_Param => Integer, Resource_Set => Resource_Set, Factory => Factory); Val2, Val3 : Int_Pools.Resource; ------------- -- Factory -- ------------- Last_Id : Natural := 0; function Factory (Param : Integer) return Integer is pragma Unreferenced (Param); begin Last_Id := Last_Id + 1; return Last_Id; end Factory; begin GNATCOLL.Traces.Parse_Config_File; Int_Pools.Set_Factory (1, Max_Elements => 2); declare Val1 : Int_Pools.Resource; begin Int_Pools.Get (Val1); A.Assert (Val1.Element.all, 1, "element from pool"); Int_Pools.Get (Val2); A.Assert (Val2.Element.all, 2, "element from pool"); -- Now Val1 goes out of scope and we release the resource end; Int_Pools.Get (Val3); A.Assert (Val3.Element.all, 1, "element from pool"); -- The following would Deadlock -- Pool.Get (Val2); Int_Pools.Free; GNATCOLL.Traces.Finalize; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/pools/test.yaml0000644000175000017500000000005413661715457022375 0ustar nicolasnicolasdescription: Basic test for GNATCOLL.Pools. gnatcoll-core-21.0.0/testsuite/tests/string_builders/0000755000175000017500000000000013661715457022576 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/string_builders/test.adb0000644000175000017500000000745613661715457024241 0ustar nicolasnicolaswith GNATCOLL.String_Builders; with Test_Assert; with Ada.Text_IO; function Test return Integer is package SB renames GNATCOLL.String_Builders; package A renames Test_Assert; package IO renames Ada.Text_IO; function C_Len (Str : SB.CString) return Integer with Import, Convention => C, External_Name => "c_strlen"; begin IO.Put_Line ("String_Builder tests"); declare S : SB.String_Builder; S_Size : constant Integer := SB.String_Builder'Size / 8; S_Object_Size : constant Integer := SB.String_Builder'Object_Size / 8; Short_String : constant String := "short string"; Long_String : constant String := "lonnnnnnnnnnnnnnnnnnnnnnnng string"; Dummy_C : Character; begin IO.Put_Line ("String_Builder size:" & S_Size'Img); IO.Put_Line ("String_Builder object size:" & S_Object_Size'Img); pragma Warnings (Off, """S"" may be referenced before it has a value"); A.Assert (SB.Length (S) = 0); A.Assert (SB.As_String (S) = ""); A.Assert (C_Len (SB.As_CString (S)) = 0); pragma Warnings (On, """S"" may be referenced before it has a value"); SB.Append (S, Short_String); SB.Append (S, 'A'); SB.Append (S, ""); A.Assert (C_Len (SB.As_CString (S)) = Short_String'Length + 1); SB.Append (S, Long_String); SB.Append (S, 'B'); SB.Append (S, Long_String); SB.Append (S, Long_String); A.Assert (SB.As_String (S), Short_String & 'A' & Long_String & 'B' & Long_String & Long_String); A.Assert (SB.Element (S, 7) = 's'); A.Assert (SB.Length (S) = C_Len (SB.As_CString (S))); SB.Set (S, "hello"); A.Assert (SB.As_String (S), "hello"); A.Assert (SB.Element (S, 5) = 'o'); A.Assert (SB.Length (S) = C_Len (SB.As_CString (S))); begin Dummy_C := SB.Element (S, 6); A.Assert (False, "no exception"); exception when Constraint_Error => A.Assert (True); when others => A.Assert (False, "wrong exception"); end; SB.Deallocate (S); end; IO.Put_Line ("Static_String_Builder tests"); declare S : SB.Static_String_Builder (30 + 1); Short_String : constant String := "0123456789"; Dummy_C : Character; S1 : SB.Static_String_Builder (1); begin pragma Warnings (Off, """S"" may be referenced before it has a value"); A.Assert (SB.Length (S) = 0); A.Assert (SB.As_String (S), ""); pragma Warnings (On, """S"" may be referenced before it has a value"); SB.Append (S, Short_String); SB.Append (S, Short_String); SB.Append (S, ""); SB.Append (S, Short_String); A.Assert (SB.Length (S) = 30); A.Assert (SB.As_String (S), Short_String & Short_String & Short_String); SB.Set (S, "a"); SB.Append (S, 'A'); A.Assert (SB.Length (S) = 2); A.Assert (SB.As_String (S) = "aA"); A.Assert (SB.Element (S, 2) = 'A'); begin Dummy_C := SB.Element (S, 3); A.Assert (False, "no exception"); exception when Constraint_Error => A.Assert (True); when others => A.Assert (False, "wrong exception"); end; begin SB.Append (S1, 'a'); A.Assert (False, "no exception"); exception when Constraint_Error => A.Assert (True); when others => A.Assert (False, "wrong exception"); end; begin SB.Append (S1, "A"); A.Assert (False, "no exception"); exception when Constraint_Error => A.Assert (True); when others => A.Assert (False, "wrong exception"); end; A.Assert (C_Len (SB.As_CString (S1)) = 0); end; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/string_builders/test_c.c0000644000175000017500000000012013661715457024214 0ustar nicolasnicolas#include int c_strlen (char *message) { return strlen (message); } gnatcoll-core-21.0.0/testsuite/tests/string_builders/test.yaml0000644000175000017500000000004013661715457024433 0ustar nicolasnicolastitle: GNATCOLL.String_Builders gnatcoll-core-21.0.0/testsuite/tests/email/0000755000175000017500000000000013661715457020466 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/email/email_data/0000755000175000017500000000000013661715457022546 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/email/email_data/test.adb0000644000175000017500000000563613661715457024207 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Calendar; with Test_Assert; with GNATCOLL.Email.Mailboxes; with GNATCOLL.Email.Utils; use GNATCOLL.Email, GNATCOLL.Email.Mailboxes, GNATCOLL.Email.Utils; with GNATCOLL.VFS; use GNATCOLL.VFS; function Test return Integer is package Cal renames Ada.Calendar; package A renames Test_Assert; Count : Natural := 0; procedure Parse_File (Filename : String); -- Parse file as mailbox procedure Parse_File (Filename : String) is Box : Mbox; Msg : Message; Addr : Address_Set.Set; T : Cal.Time; pragma Unreferenced (Addr, T); begin Open (Box, Filename => Create (+Filename)); declare Curs : GNATCOLL.Email.Mailboxes.Cursor'Class := First (Box); begin while Has_Element (Curs) loop Get_Message (Curs, Box, Msg); if Msg /= Null_Message then Addr := Get_Recipients (Msg); T := Date_From_Envelope (Msg); end if; Next (Curs, Box); Count := Count + 1; end loop; end; end Parse_File; begin Parse_File ("tea_party.mbx"); A.Assert (Count, 95, "expected number of messages"); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/email/email_data/test.yaml0000644000175000017500000000016013661715457024406 0ustar nicolasnicolasdescription: email data and parse test. data: - "*.mbx" # The test set is derived from mailutils testsuite gnatcoll-core-21.0.0/testsuite/tests/email/email_data/tea_party.mbx0000644000175000017500000012703213661715457025253 0ustar nicolasnicolasFrom March Hare Received: (from hare@wonder.land) by wonder.land id 3301 for alice@wonder.land; Mon, 29 Jul 2002 22:00:06 +0100 Date: Mon, 29 Jul 2002 22:00:01 +0100 From: March Hare Message-Id: <200207292200.3301@wonder.land> To: Alice Subject: Invitation X-IMAPbase: 0 1 X-Envelope-Date: Mon Jul 29 22:00:08 2002 X-Envelope-Sender: hare@wonder.land Have some wine From Alice Received: (from alice@wonder.land) by wonder.land id 3302 for hare@wonder.land; Mon, 29 Jul 2002 22:00:07 +0100 Date: Mon, 29 Jul 2002 22:00:02 +0100 From: Alice Message-Id: <200207292200.3302@wonder.land> To: March Hare Subject: Re: Invitation X-Envelope-Date: Mon Jul 29 22:00:09 2002 X-Envelope-Sender: alice@wonder.land I don't see any wine From March Hare Received: (from hare@wonder.land) by wonder.land id 3303 for alice@wonder.land; Mon, 29 Jul 2002 22:00:08 +0100 Date: Mon, 29 Jul 2002 22:00:03 +0100 From: March Hare Message-Id: <200207292200.3303@wonder.land> To: Alice Subject: Re: Invitation X-Envelope-Date: Mon Jul 29 22:00:10 2002 X-Envelope-Sender: hare@wonder.land There isn't any From Alice Received: (from alice@wonder.land) by wonder.land id 3304 for hare@wonder.land; Mon, 29 Jul 2002 22:00:09 +0100 Date: Mon, 29 Jul 2002 22:00:04 +0100 From: Alice Message-Id: <200207292200.3304@wonder.land> To: March Hare Subject: Re: Invitation X-Envelope-Date: Mon Jul 29 22:00:11 2002 X-Envelope-Sender: alice@wonder.land Then it wasn't very civil of you to offer it From March Hare Received: (from hare@wonder.land) by wonder.land id 3305 for alice@wonder.land; Mon, 29 Jul 2002 22:00:10 +0100 Date: Mon, 29 Jul 2002 22:00:05 +0100 From: March Hare Message-Id: <200207292200.3305@wonder.land> To: Alice Subject: Re: Invitation X-Envelope-Date: Mon Jul 29 22:00:12 2002 X-Envelope-Sender: hare@wonder.land It wasn't very civil of you to sit down without being invited From Alice Received: (from alice@wonder.land) by wonder.land id 3306 for hare@wonder.land; Mon, 29 Jul 2002 22:00:11 +0100 Date: Mon, 29 Jul 2002 22:00:06 +0100 From: Alice Message-Id: <200207292200.3306@wonder.land> To: March Hare Subject: Re: Invitation X-Envelope-Date: Mon Jul 29 22:00:13 2002 X-Envelope-Sender: alice@wonder.land I didn't know it was YOUR table, it's laid for a great many more than three. From March Hare Received: (from hare@wonder.land) by wonder.land id 3307 for alice@wonder.land; Mon, 29 Jul 2002 22:00:12 +0100 Date: Mon, 29 Jul 2002 22:00:07 +0100 From: March Hare Message-Id: <200207292200.3307@wonder.land> To: Alice Subject: Personal remark X-Envelope-Date: Mon Jul 29 22:00:14 2002 X-Envelope-Sender: hare@wonder.land Your hair wants cutting From Alice Received: (from alice@wonder.land) by wonder.land id 3308 for hare@wonder.land; Mon, 29 Jul 2002 22:00:13 +0100 Date: Mon, 29 Jul 2002 22:00:08 +0100 From: Alice Message-Id: <200207292200.3308@wonder.land> To: March Hare Subject: Re: Personal remark X-Envelope-Date: Mon Jul 29 22:00:15 2002 X-Envelope-Sender: alice@wonder.land You should learn not to make personal remarks, it's very rude. From March Hare Received: (from hare@wonder.land) by wonder.land id 3309 for alice@wonder.land; Mon, 29 Jul 2002 22:00:14 +0100 Date: Mon, 29 Jul 2002 22:00:09 +0100 From: March Hare Message-Id: <200207292200.3309@wonder.land> To: Alice Subject: Riddle (was Re: Personal remark) X-Envelope-Date: Mon Jul 29 22:00:16 2002 X-Envelope-Sender: hare@wonder.land Why is a raven like a writing-desk? From Alice Received: (from alice@wonder.land) by wonder.land id 3310 for hare@wonder.land; Mon, 29 Jul 2002 22:00:15 +0100 Date: Mon, 29 Jul 2002 22:00:10 +0100 From: Alice Message-Id: <200207292200.3310@wonder.land> To: March Hare Subject: Re: Riddle X-Envelope-Date: Mon Jul 29 22:00:17 2002 X-Envelope-Sender: alice@wonder.land I believe I can guess that From March Hare Received: (from hare@wonder.land) by wonder.land id 3311 for alice@wonder.land; Mon, 29 Jul 2002 22:00:16 +0100 Date: Mon, 29 Jul 2002 22:00:11 +0100 From: March Hare Message-Id: <200207292200.3311@wonder.land> To: Alice Subject: Re: Riddle X-Envelope-Date: Mon Jul 29 22:00:18 2002 X-Envelope-Sender: hare@wonder.land Do you mean that you think you can find out the answer to it? From Alice Received: (from alice@wonder.land) by wonder.land id 3312 for hare@wonder.land; Mon, 29 Jul 2002 22:00:17 +0100 Date: Mon, 29 Jul 2002 22:00:12 +0100 From: Alice Message-Id: <200207292200.3312@wonder.land> To: March Hare Subject: Re: Riddle X-Envelope-Date: Mon Jul 29 22:00:19 2002 X-Envelope-Sender: alice@wonder.land Exactly so From March Hare Received: (from hare@wonder.land) by wonder.land id 3313 for alice@wonder.land; Mon, 29 Jul 2002 22:00:18 +0100 Date: Mon, 29 Jul 2002 22:00:13 +0100 From: March Hare Message-Id: <200207292200.3313@wonder.land> To: Alice Subject: Be specific (was Re: Riddle) X-Envelope-Date: Mon Jul 29 22:00:20 2002 X-Envelope-Sender: hare@wonder.land Then you should say what you mean From Alice Received: (from alice@wonder.land) by wonder.land id 3314 for hare@wonder.land; Mon, 29 Jul 2002 22:00:19 +0100 Date: Mon, 29 Jul 2002 22:00:14 +0100 From: Alice Message-Id: <200207292200.3314@wonder.land> To: March Hare Subject: Re: Be specific X-Envelope-Date: Mon Jul 29 22:00:21 2002 X-Envelope-Sender: alice@wonder.land I do, at least--at least I mean what I say--that's the same thing, you know. From Hatter Received: (from hatter@wonder.land) by wonder.land id 3315 for alice@wonder.land; Mon, 29 Jul 2002 22:00:20 +0100 Date: Mon, 29 Jul 2002 22:00:15 +0100 From: Hatter Message-Id: <200207292200.3315@wonder.land> To: Alice Subject: Re: Be specific X-Envelope-Date: Mon Jul 29 22:00:22 2002 X-Envelope-Sender: hatter@wonder.land Not the same thing a bit! You might just as well say that "I see what I eat" is the same thing as "I eat what I see"! From March Hare Received: (from hare@wonder.land) by wonder.land id 3316 for alice@wonder.land; Mon, 29 Jul 2002 22:00:21 +0100 Date: Mon, 29 Jul 2002 22:00:16 +0100 From: March Hare Message-Id: <200207292200.3316@wonder.land> To: Alice Subject: Re: Be specific X-Envelope-Date: Mon Jul 29 22:00:23 2002 X-Envelope-Sender: hare@wonder.land You might just as well say, that "I like what I get" is the same thing as "I get what I like"! From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3317 for alice@wonder.land; Mon, 29 Jul 2002 22:00:22 +0100 Date: Mon, 29 Jul 2002 22:00:17 +0100 From: Dormouse Message-Id: <200207292200.3317@wonder.land> To: Alice Subject: Re: Be specific X-Envelope-Date: Mon Jul 29 22:00:24 2002 X-Envelope-Sender: dormouse@wonder.land You might just as well say, who seemed to be talking in his sleep, `that "I breathe when I sleep" is the same thing as "I sleep when I breathe"! From Hatter Received: (from hatter@wonder.land) by wonder.land id 3318 for dormouse@wonder.land; Mon, 29 Jul 2002 22:00:23 +0100 Date: Mon, 29 Jul 2002 22:00:18 +0100 From: Hatter Message-Id: <200207292200.3318@wonder.land> To: Dormouse Subject: Re: Be specific X-Envelope-Date: Mon Jul 29 22:00:25 2002 X-Envelope-Sender: hatter@wonder.land It IS the same thing with you From Hatter Received: (from hatter@wonder.land) by wonder.land id 3319 for alice@wonder.land; Mon, 29 Jul 2002 22:00:24 +0100 Date: Mon, 29 Jul 2002 22:00:19 +0100 From: Hatter Message-Id: <200207292200.3319@wonder.land> To: Alice Subject: Watch X-Envelope-Date: Mon Jul 29 22:00:26 2002 X-Envelope-Sender: hatter@wonder.land What day of the month is it? From Alice Received: (from alice@wonder.land) by wonder.land id 3320 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:25 +0100 Date: Mon, 29 Jul 2002 22:00:20 +0100 From: Alice Message-Id: <200207292200.3320@wonder.land> To: Hatter Subject: Re: Watch X-Envelope-Date: Mon Jul 29 22:00:27 2002 X-Envelope-Sender: alice@wonder.land The fourth. From Hatter Received: (from hatter@wonder.land) by wonder.land id 3321 for hare@wonder.land; Mon, 29 Jul 2002 22:00:26 +0100 Date: Mon, 29 Jul 2002 22:00:21 +0100 From: Hatter Message-Id: <200207292200.3321@wonder.land> To: March Hare Subject: Re: Watch X-Envelope-Date: Mon Jul 29 22:00:28 2002 X-Envelope-Sender: hatter@wonder.land Two days wrong! I told you butter wouldn't suit the works! From March Hare Received: (from hare@wonder.land) by wonder.land id 3322 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:27 +0100 Date: Mon, 29 Jul 2002 22:00:22 +0100 From: March Hare Message-Id: <200207292200.3322@wonder.land> To: Hatter Subject: Re: Watch X-Envelope-Date: Mon Jul 29 22:00:29 2002 X-Envelope-Sender: hare@wonder.land It was the BEST butter From Hatter Received: (from hatter@wonder.land) by wonder.land id 3323 for hare@wonder.land; Mon, 29 Jul 2002 22:00:28 +0100 Date: Mon, 29 Jul 2002 22:00:23 +0100 From: Hatter Message-Id: <200207292200.3323@wonder.land> To: March Hare Subject: Re: Watch X-Envelope-Date: Mon Jul 29 22:00:30 2002 X-Envelope-Sender: hatter@wonder.land Yes, but some crumbs must have got in as well, you shouldn't have put it in with the bread-knife. From March Hare Received: (from hare@wonder.land) by wonder.land id 3324 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:29 +0100 Date: Mon, 29 Jul 2002 22:00:24 +0100 From: March Hare Message-Id: <200207292200.3324@wonder.land> To: Hatter Subject: Re: Watch X-Envelope-Date: Mon Jul 29 22:00:31 2002 X-Envelope-Sender: hare@wonder.land It was the BEST butter, you know. From Alice Received: (from alice@wonder.land) by wonder.land id 3325 for tea.party@wonder.land; Mon, 29 Jul 2002 22:00:30 +0100 Date: Mon, 29 Jul 2002 22:00:25 +0100 From: Alice Message-Id: <200207292200.3325@wonder.land> To: Mad Tea Party Subject: Funny watch (was Re: Watch) X-Envelope-Date: Mon Jul 29 22:00:32 2002 X-Envelope-Sender: alice@wonder.land What a funny watch! It tells the day of the month, and doesn't tell what o'clock it is! From Hatter Received: (from hatter@wonder.land) by wonder.land id 3326 for alice@wonder.land; Mon, 29 Jul 2002 22:00:31 +0100 Date: Mon, 29 Jul 2002 22:00:26 +0100 From: Hatter Message-Id: <200207292200.3326@wonder.land> To: Alice Subject: Re: Funny watch X-Envelope-Date: Mon Jul 29 22:00:33 2002 X-Envelope-Sender: hatter@wonder.land Why should it? Does YOUR watch tell you what year it is? From Alice Received: (from alice@wonder.land) by wonder.land id 3327 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:32 +0100 Date: Mon, 29 Jul 2002 22:00:27 +0100 From: Alice Message-Id: <200207292200.3327@wonder.land> To: Hatter Subject: Re: Funny watch X-Envelope-Date: Mon Jul 29 22:00:34 2002 X-Envelope-Sender: alice@wonder.land Of course not, but that's because it stays the same year for such a long time together. From Hatter Received: (from hatter@wonder.land) by wonder.land id 3328 for alice@wonder.land; Mon, 29 Jul 2002 22:00:33 +0100 Date: Mon, 29 Jul 2002 22:00:28 +0100 From: Hatter Message-Id: <200207292200.3328@wonder.land> To: Alice Subject: Re: Funny watch X-Envelope-Date: Mon Jul 29 22:00:35 2002 X-Envelope-Sender: hatter@wonder.land Which is just the case with MINE From Alice Received: (from alice@wonder.land) by wonder.land id 3329 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:34 +0100 Date: Mon, 29 Jul 2002 22:00:29 +0100 From: Alice Message-Id: <200207292200.3329@wonder.land> To: Hatter Subject: Re: Funny watch X-Envelope-Date: Mon Jul 29 22:00:36 2002 X-Envelope-Sender: alice@wonder.land I don't quite understand you From Hatter Received: (from hatter@wonder.land) by wonder.land id 3330 for tea.party@wonder.land; Mon, 29 Jul 2002 22:00:35 +0100 Date: Mon, 29 Jul 2002 22:00:30 +0100 From: Hatter Message-Id: <200207292200.3330@wonder.land> To: Mad Tea Party Subject: Remark X-Envelope-Date: Mon Jul 29 22:00:37 2002 X-Envelope-Sender: hatter@wonder.land The Dormouse is asleep again From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3331 for nobody@wonder.land; Mon, 29 Jul 2002 22:00:36 +0100 Date: Mon, 29 Jul 2002 22:00:31 +0100 From: Dormouse Message-Id: <200207292200.3331@wonder.land> To: Nobody at all Subject: Re: Remark X-Envelope-Date: Mon Jul 29 22:00:38 2002 X-Envelope-Sender: dormouse@wonder.land Of course, of course; just what I was going to remark myself. From Hatter Received: (from hatter@wonder.land) by wonder.land id 3332 for alice@wonder.land; Mon, 29 Jul 2002 22:00:37 +0100 Date: Mon, 29 Jul 2002 22:00:32 +0100 From: Hatter Message-Id: <200207292200.3332@wonder.land> To: Alice Subject: Re: Riddle X-Envelope-Date: Mon Jul 29 22:00:39 2002 X-Envelope-Sender: hatter@wonder.land Have you guessed the riddle yet? From Alice Received: (from alice@wonder.land) by wonder.land id 3333 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:38 +0100 Date: Mon, 29 Jul 2002 22:00:33 +0100 From: Alice Message-Id: <200207292200.3333@wonder.land> To: Hatter Subject: Re: Riddle X-Envelope-Date: Mon Jul 29 22:00:40 2002 X-Envelope-Sender: alice@wonder.land No, I give it up, what's the answer? From Hatter Received: (from hatter@wonder.land) by wonder.land id 3334 for alice@wonder.land; Mon, 29 Jul 2002 22:00:39 +0100 Date: Mon, 29 Jul 2002 22:00:34 +0100 From: Hatter Message-Id: <200207292200.3334@wonder.land> To: Alice Subject: Re: Riddle X-Envelope-Date: Mon Jul 29 22:00:41 2002 X-Envelope-Sender: hatter@wonder.land I haven't the slightest idea From March Hare Received: (from hare@wonder.land) by wonder.land id 3335 for alice@wonder.land; Mon, 29 Jul 2002 22:00:40 +0100 Date: Mon, 29 Jul 2002 22:00:35 +0100 From: March Hare Message-Id: <200207292200.3335@wonder.land> To: Alice Subject: Re: Riddle X-Envelope-Date: Mon Jul 29 22:00:42 2002 X-Envelope-Sender: hare@wonder.land Nor I From Alice Received: (from alice@wonder.land) by wonder.land id 3336 for tea.party@wonder.land; Mon, 29 Jul 2002 22:00:41 +0100 Date: Mon, 29 Jul 2002 22:00:36 +0100 From: Alice Message-Id: <200207292200.3336@wonder.land> To: Mad Tea Party Subject: Re: Riddle X-Envelope-Date: Mon Jul 29 22:00:43 2002 X-Envelope-Sender: alice@wonder.land I think you might do something better with the time, than waste it in asking riddles that have no answers. From Hatter Received: (from hatter@wonder.land) by wonder.land id 3337 for alice@wonder.land; Mon, 29 Jul 2002 22:00:42 +0100 Date: Mon, 29 Jul 2002 22:00:37 +0100 From: Hatter Message-Id: <200207292200.3337@wonder.land> To: Alice Subject: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:44 2002 X-Envelope-Sender: hatter@wonder.land If you knew Time as well as I do, you wouldn't talk about wasting IT. It's HIM. From Alice Received: (from alice@wonder.land) by wonder.land id 3338 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:43 +0100 Date: Mon, 29 Jul 2002 22:00:38 +0100 From: Alice Message-Id: <200207292200.3338@wonder.land> To: Hatter Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:45 2002 X-Envelope-Sender: alice@wonder.land I don't know what you mean From Hatter Received: (from hatter@wonder.land) by wonder.land id 3339 for alice@wonder.land; Mon, 29 Jul 2002 22:00:44 +0100 Date: Mon, 29 Jul 2002 22:00:39 +0100 From: Hatter Message-Id: <200207292200.3339@wonder.land> To: Alice Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:46 2002 X-Envelope-Sender: hatter@wonder.land Of course you don't! I dare say you never even spoke to Time! From Alice Received: (from alice@wonder.land) by wonder.land id 3340 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:45 +0100 Date: Mon, 29 Jul 2002 22:00:40 +0100 From: Alice Message-Id: <200207292200.3340@wonder.land> To: Hatter Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:47 2002 X-Envelope-Sender: alice@wonder.land Perhaps not, but I know I have to beat time when I learn music. From Hatter Received: (from hatter@wonder.land) by wonder.land id 3341 for alice@wonder.land; Mon, 29 Jul 2002 22:00:46 +0100 Date: Mon, 29 Jul 2002 22:00:41 +0100 From: Hatter Message-Id: <200207292200.3341@wonder.land> To: Alice Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:48 2002 X-Envelope-Sender: hatter@wonder.land Ah! that accounts for it. He won't stand beating. Now, if you only kept on good terms with him, he'd do almost anything you liked with the clock. For instance, suppose it were nine o'clock in the morning, just time to begin lessons: you'd only have to whisper a hint to Time, and round goes the clock in a twinkling! Half-past one, time for dinner! From March Hare Received: (from hare@wonder.land) by wonder.land id 3342 for hare@wonder.land; Mon, 29 Jul 2002 22:00:47 +0100 Date: Mon, 29 Jul 2002 22:00:42 +0100 From: March Hare Message-Id: <200207292200.3342@wonder.land> To: March Hare Subject: Thought apart Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:49 2002 X-Envelope-Sender: hare@wonder.land I only wish it was From Alice Received: (from alice@wonder.land) by wonder.land id 3343 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:48 +0100 Date: Mon, 29 Jul 2002 22:00:43 +0100 From: Alice Message-Id: <200207292200.3343@wonder.land> To: Hatter Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:50 2002 X-Envelope-Sender: alice@wonder.land That would be grand, certainly, but then--I shouldn't be hungry for it, you know. From Hatter Received: (from hatter@wonder.land) by wonder.land id 3344 for alice@wonder.land; Mon, 29 Jul 2002 22:00:49 +0100 Date: Mon, 29 Jul 2002 22:00:44 +0100 From: Hatter Message-Id: <200207292200.3344@wonder.land> To: Alice Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:51 2002 X-Envelope-Sender: hatter@wonder.land Not at first, perhaps, but you could keep it to half-past one as long as you liked. From Alice Received: (from alice@wonder.land) by wonder.land id 3345 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:50 +0100 Date: Mon, 29 Jul 2002 22:00:45 +0100 From: Alice Message-Id: <200207292200.3345@wonder.land> To: Hatter Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:52 2002 X-Envelope-Sender: alice@wonder.land Is that the way YOU manage? From Hatter Received: (from hatter@wonder.land) by wonder.land id 3346 for alice@wonder.land; Mon, 29 Jul 2002 22:00:51 +0100 Date: Mon, 29 Jul 2002 22:00:46 +0100 From: Hatter Message-Id: <200207292200.3346@wonder.land> To: Alice Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:53 2002 X-Envelope-Sender: hatter@wonder.land Not I! We quarrelled last March--just before HE went mad, you know-- it was at the great concert given by the Queen of Hearts, and I had to sing "Twinkle, twinkle, little bat! How I wonder what you're at!" You know the song, perhaps? From Alice Received: (from alice@wonder.land) by wonder.land id 3347 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:52 +0100 Date: Mon, 29 Jul 2002 22:00:47 +0100 From: Alice Message-Id: <200207292200.3347@wonder.land> To: Hatter Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:54 2002 X-Envelope-Sender: alice@wonder.land I've heard something like it From Hatter Received: (from hatter@wonder.land) by wonder.land id 3348 for alice@wonder.land; Mon, 29 Jul 2002 22:00:53 +0100 Date: Mon, 29 Jul 2002 22:00:48 +0100 From: Hatter Message-Id: <200207292200.3348@wonder.land> To: Alice Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:55 2002 X-Envelope-Sender: hatter@wonder.land It goes on, you know in this way:-- "Up above the world you fly, Like a tea-tray in the sky. Twinkle, twinkle--"' From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3349 for nobody@wonder.land; Mon, 29 Jul 2002 22:00:54 +0100 Date: Mon, 29 Jul 2002 22:00:49 +0100 From: Dormouse Message-Id: <200207292200.3349@wonder.land> To: Nobody at all Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:56 2002 X-Envelope-Sender: dormouse@wonder.land Twinkle, twinkle, twinkle, twinkle... From Hatter Received: (from hatter@wonder.land) by wonder.land id 3350 for alice@wonder.land; Mon, 29 Jul 2002 22:00:55 +0100 Date: Mon, 29 Jul 2002 22:00:50 +0100 From: Hatter Message-Id: <200207292200.3350@wonder.land> To: Alice Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:57 2002 X-Envelope-Sender: hatter@wonder.land Well, I'd hardly finished the first verse, when the Queen jumped up and bawled out, "He's murdering the time! Off with his head!" From Alice Received: (from alice@wonder.land) by wonder.land id 3351 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:56 +0100 Date: Mon, 29 Jul 2002 22:00:51 +0100 From: Alice Message-Id: <200207292200.3351@wonder.land> To: Hatter Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:58 2002 X-Envelope-Sender: alice@wonder.land How dreadfully savage! From Hatter Received: (from hatter@wonder.land) by wonder.land id 3352 for alice@wonder.land; Mon, 29 Jul 2002 22:00:57 +0100 Date: Mon, 29 Jul 2002 22:00:52 +0100 From: Hatter Message-Id: <200207292200.3352@wonder.land> To: Alice Subject: Re: Discussing Time X-Envelope-Date: Mon Jul 29 22:00:59 2002 X-Envelope-Sender: hatter@wonder.land And ever since that, he won't do a thing I ask! It's always six o'clock now. From Alice Received: (from alice@wonder.land) by wonder.land id 3353 for hatter@wonder.land; Mon, 29 Jul 2002 22:00:58 +0100 Date: Mon, 29 Jul 2002 22:00:53 +0100 From: Alice Message-Id: <200207292200.3353@wonder.land> To: Hatter Subject: A guess (was Re: Discussing Time) X-Envelope-Date: Mon Jul 29 22:01:00 2002 X-Envelope-Sender: alice@wonder.land Is that the reason so many tea-things are put out here? From Hatter Received: (from hatter@wonder.land) by wonder.land id 3354 for alice@wonder.land; Mon, 29 Jul 2002 22:00:59 +0100 Date: Mon, 29 Jul 2002 22:00:54 +0100 From: Hatter Message-Id: <200207292200.3354@wonder.land> To: Alice Subject: Re: A guess X-Envelope-Date: Mon Jul 29 22:01:01 2002 X-Envelope-Sender: hatter@wonder.land Yes, that's it. It's always tea-time, and we've no time to wash the things between whiles. From Alice Received: (from alice@wonder.land) by wonder.land id 3355 for hatter@wonder.land; Mon, 29 Jul 2002 22:01:00 +0100 Date: Mon, 29 Jul 2002 22:00:55 +0100 From: Alice Message-Id: <200207292200.3355@wonder.land> To: Hatter Subject: Re: A guess X-Envelope-Date: Mon Jul 29 22:01:02 2002 X-Envelope-Sender: alice@wonder.land Then you keep moving round, I suppose? From Hatter Received: (from hatter@wonder.land) by wonder.land id 3356 for alice@wonder.land; Mon, 29 Jul 2002 22:01:01 +0100 Date: Mon, 29 Jul 2002 22:00:56 +0100 From: Hatter Message-Id: <200207292200.3356@wonder.land> To: Alice Subject: Re: A guess X-Envelope-Date: Mon Jul 29 22:01:03 2002 X-Envelope-Sender: hatter@wonder.land Exactly so, as the things get used up. From Alice Received: (from alice@wonder.land) by wonder.land id 3357 for hatter@wonder.land; Mon, 29 Jul 2002 22:01:02 +0100 Date: Mon, 29 Jul 2002 22:00:57 +0100 From: Alice Message-Id: <200207292200.3357@wonder.land> To: Hatter Subject: Re: A guess X-Envelope-Date: Mon Jul 29 22:01:04 2002 X-Envelope-Sender: alice@wonder.land But what happens when you come to the beginning again? From March Hare Received: (from hare@wonder.land) by wonder.land id 3358 for tea.party@wonder.land; Mon, 29 Jul 2002 22:01:03 +0100 Date: Mon, 29 Jul 2002 22:00:58 +0100 From: March Hare Message-Id: <200207292200.3358@wonder.land> To: Mad Tea Party Subject: Request for a story (was Re: A guess) X-Envelope-Date: Mon Jul 29 22:01:05 2002 X-Envelope-Sender: hare@wonder.land Suppose we change the subject, I'm getting tired of this. I vote the young lady tells us a story. From Alice Received: (from alice@wonder.land) by wonder.land id 3359 for hare@wonder.land; Mon, 29 Jul 2002 22:01:04 +0100 Date: Mon, 29 Jul 2002 22:00:59 +0100 From: Alice Message-Id: <200207292200.3359@wonder.land> To: March Hare Subject: Re: Request for a story X-Envelope-Date: Mon Jul 29 22:01:06 2002 X-Envelope-Sender: alice@wonder.land I'm afraid I don't know one From Hatter Received: (from hatter@wonder.land) by wonder.land id 3360 for tea.party@wonder.land; Mon, 29 Jul 2002 22:01:05 +0100 Date: Mon, 29 Jul 2002 22:01:00 +0100 From: Hatter Message-Id: <200207292201.3360@wonder.land> To: Mad Tea Party Cc: Dormouse Subject: Re: Request for a story X-Envelope-Date: Mon Jul 29 22:01:07 2002 X-Envelope-Sender: hatter@wonder.land Then the Dormouse shall! Wake up, Dormouse! From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3361 for tea.party@wonder.land; Mon, 29 Jul 2002 22:01:06 +0100 Date: Mon, 29 Jul 2002 22:01:01 +0100 From: Dormouse Message-Id: <200207292201.3361@wonder.land> To: Mad Tea Party Subject: Re: Request for a story X-Envelope-Date: Mon Jul 29 22:01:08 2002 X-Envelope-Sender: dormouse@wonder.land I wasn't asleep. I heard every word you fellows were saying. From March Hare Received: (from hare@wonder.land) by wonder.land id 3362 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:07 +0100 Date: Mon, 29 Jul 2002 22:01:02 +0100 From: March Hare Message-Id: <200207292201.3362@wonder.land> To: Dormouse Subject: Re: Request for a story X-Envelope-Date: Mon Jul 29 22:01:09 2002 X-Envelope-Sender: hare@wonder.land Tell us a story! From Alice Received: (from alice@wonder.land) by wonder.land id 3363 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:08 +0100 Date: Mon, 29 Jul 2002 22:01:03 +0100 From: Alice Message-Id: <200207292201.3363@wonder.land> To: Dormouse Subject: Re: Request for a story X-Envelope-Date: Mon Jul 29 22:01:10 2002 X-Envelope-Sender: alice@wonder.land Yes, please do! From Hatter Received: (from hatter@wonder.land) by wonder.land id 3364 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:09 +0100 Date: Mon, 29 Jul 2002 22:01:04 +0100 From: Hatter Message-Id: <200207292201.3364@wonder.land> To: Dormouse Subject: Re: Request for a story X-Envelope-Date: Mon Jul 29 22:01:11 2002 X-Envelope-Sender: hatter@wonder.land And be quick about it, or you'll be asleep again before it's done. From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3365 for tea.party@wonder.land; Mon, 29 Jul 2002 22:01:10 +0100 Date: Mon, 29 Jul 2002 22:01:05 +0100 From: Dormouse Message-Id: <200207292201.3365@wonder.land> To: Mad Tea Party Subject: Story (was Re: Request for a story) X-Envelope-Date: Mon Jul 29 22:01:12 2002 X-Envelope-Sender: dormouse@wonder.land Once upon a time there were three little sisters, and their names were Elsie, Lacie, and Tillie; and they lived at the bottom of a well-- From Alice Received: (from alice@wonder.land) by wonder.land id 3366 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:11 +0100 Date: Mon, 29 Jul 2002 22:01:06 +0100 From: Alice Message-Id: <200207292201.3366@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:13 2002 X-Envelope-Sender: alice@wonder.land What did they live on? From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3367 for alice@wonder.land; Mon, 29 Jul 2002 22:01:12 +0100 Date: Mon, 29 Jul 2002 22:01:07 +0100 From: Dormouse Message-Id: <200207292201.3367@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:14 2002 X-Envelope-Sender: dormouse@wonder.land They lived on treacle From Alice Received: (from alice@wonder.land) by wonder.land id 3368 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:13 +0100 Date: Mon, 29 Jul 2002 22:01:08 +0100 From: Alice Message-Id: <200207292201.3368@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:15 2002 X-Envelope-Sender: alice@wonder.land They couldn't have done that, you know, they'd have been ill From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3369 for alice@wonder.land; Mon, 29 Jul 2002 22:01:14 +0100 Date: Mon, 29 Jul 2002 22:01:09 +0100 From: Dormouse Message-Id: <200207292201.3369@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:16 2002 X-Envelope-Sender: dormouse@wonder.land So they were, VERY ill. From Alice Received: (from alice@wonder.land) by wonder.land id 3370 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:15 +0100 Date: Mon, 29 Jul 2002 22:01:10 +0100 From: Alice Message-Id: <200207292201.3370@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:17 2002 X-Envelope-Sender: alice@wonder.land But why did they live at the bottom of a well? From March Hare Received: (from hare@wonder.land) by wonder.land id 3371 for alice@wonder.land; Mon, 29 Jul 2002 22:01:16 +0100 Date: Mon, 29 Jul 2002 22:01:11 +0100 From: March Hare Message-Id: <200207292201.3371@wonder.land> To: Alice Subject: Remark X-Envelope-Date: Mon Jul 29 22:01:18 2002 X-Envelope-Sender: hare@wonder.land Take some more tea From Alice Received: (from alice@wonder.land) by wonder.land id 3372 for hare@wonder.land; Mon, 29 Jul 2002 22:01:17 +0100 Date: Mon, 29 Jul 2002 22:01:12 +0100 From: Alice Message-Id: <200207292201.3372@wonder.land> To: March Hare Subject: Re: Remark X-Envelope-Date: Mon Jul 29 22:01:19 2002 X-Envelope-Sender: alice@wonder.land I've had nothing yet, so I can't take more. From Hatter Received: (from hatter@wonder.land) by wonder.land id 3373 for alice@wonder.land; Mon, 29 Jul 2002 22:01:18 +0100 Date: Mon, 29 Jul 2002 22:01:13 +0100 From: Hatter Message-Id: <200207292201.3373@wonder.land> To: Alice Subject: Re: Remark X-Envelope-Date: Mon Jul 29 22:01:20 2002 X-Envelope-Sender: hatter@wonder.land You mean you can't take LESS, it's very easy to take MORE than nothing. From Alice Received: (from alice@wonder.land) by wonder.land id 3374 for hatter@wonder.land; Mon, 29 Jul 2002 22:01:19 +0100 Date: Mon, 29 Jul 2002 22:01:14 +0100 From: Alice Message-Id: <200207292201.3374@wonder.land> To: Hatter Subject: Personal remark X-Envelope-Date: Mon Jul 29 22:01:21 2002 X-Envelope-Sender: alice@wonder.land Nobody asked YOUR opinion From Hatter Received: (from hatter@wonder.land) by wonder.land id 3375 for alice@wonder.land; Mon, 29 Jul 2002 22:01:20 +0100 Date: Mon, 29 Jul 2002 22:01:15 +0100 From: Hatter Message-Id: <200207292201.3375@wonder.land> To: Alice Subject: Re: Personal remark X-Envelope-Date: Mon Jul 29 22:01:22 2002 X-Envelope-Sender: hatter@wonder.land Who's making personal remarks now? From Alice Received: (from alice@wonder.land) by wonder.land id 3376 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:21 +0100 Date: Mon, 29 Jul 2002 22:01:16 +0100 From: Alice Message-Id: <200207292201.3376@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:23 2002 X-Envelope-Sender: alice@wonder.land Why did they live at the bottom of a well? From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3377 for alice@wonder.land; Mon, 29 Jul 2002 22:01:22 +0100 Date: Mon, 29 Jul 2002 22:01:17 +0100 From: Dormouse Message-Id: <200207292201.3377@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:24 2002 X-Envelope-Sender: dormouse@wonder.land It was a treacle-well. From Alice Received: (from alice@wonder.land) by wonder.land id 3378 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:23 +0100 Date: Mon, 29 Jul 2002 22:01:18 +0100 From: Alice Message-Id: <200207292201.3378@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:25 2002 X-Envelope-Sender: alice@wonder.land There's no such thing! From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3379 for alice@wonder.land; Mon, 29 Jul 2002 22:01:24 +0100 Date: Mon, 29 Jul 2002 22:01:19 +0100 From: Dormouse Message-Id: <200207292201.3379@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:26 2002 X-Envelope-Sender: dormouse@wonder.land If you can't be civil, you'd better finish the story for yourself. From Alice Received: (from alice@wonder.land) by wonder.land id 3380 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:25 +0100 Date: Mon, 29 Jul 2002 22:01:20 +0100 From: Alice Message-Id: <200207292201.3380@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:27 2002 X-Envelope-Sender: alice@wonder.land No, please go on! I won't interrupt again. I dare say there may be ONE. From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3381 for alice@wonder.land; Mon, 29 Jul 2002 22:01:26 +0100 Date: Mon, 29 Jul 2002 22:01:21 +0100 From: Dormouse Message-Id: <200207292201.3381@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:28 2002 X-Envelope-Sender: dormouse@wonder.land One, indeed! And so these three little sisters--they were learning to draw, you know-- From Alice Received: (from alice@wonder.land) by wonder.land id 3382 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:27 +0100 Date: Mon, 29 Jul 2002 22:01:22 +0100 From: Alice Message-Id: <200207292201.3382@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:29 2002 X-Envelope-Sender: alice@wonder.land What did they draw? From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3383 for alice@wonder.land; Mon, 29 Jul 2002 22:01:28 +0100 Date: Mon, 29 Jul 2002 22:01:23 +0100 From: Dormouse Message-Id: <200207292201.3383@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:30 2002 X-Envelope-Sender: dormouse@wonder.land Treacle. From Hatter Received: (from hatter@wonder.land) by wonder.land id 3384 for tea.party@wonder.land; Mon, 29 Jul 2002 22:01:29 +0100 Date: Mon, 29 Jul 2002 22:01:24 +0100 From: Hatter Message-Id: <200207292201.3384@wonder.land> To: Mad Tea Party Subject: Let's move X-Envelope-Date: Mon Jul 29 22:01:31 2002 X-Envelope-Sender: hatter@wonder.land I want a clean cup, let's all move one place on. From Alice Received: (from alice@wonder.land) by wonder.land id 3385 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:30 +0100 Date: Mon, 29 Jul 2002 22:01:25 +0100 From: Alice Message-Id: <200207292201.3385@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:32 2002 X-Envelope-Sender: alice@wonder.land But I don't understand. Where did they draw the treacle from? From Hatter Received: (from hatter@wonder.land) by wonder.land id 3386 for alice@wonder.land; Mon, 29 Jul 2002 22:01:31 +0100 Date: Mon, 29 Jul 2002 22:01:26 +0100 From: Hatter Message-Id: <200207292201.3386@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:33 2002 X-Envelope-Sender: hatter@wonder.land You can draw water out of a water-well, so I should think you could draw treacle out of a treacle-well--eh, stupid? From Alice Received: (from alice@wonder.land) by wonder.land id 3387 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:32 +0100 Date: Mon, 29 Jul 2002 22:01:27 +0100 From: Alice Message-Id: <200207292201.3387@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:34 2002 X-Envelope-Sender: alice@wonder.land But they were IN the well From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3388 for alice@wonder.land; Mon, 29 Jul 2002 22:01:33 +0100 Date: Mon, 29 Jul 2002 22:01:28 +0100 From: Dormouse Message-Id: <200207292201.3388@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:35 2002 X-Envelope-Sender: dormouse@wonder.land Of course they were -- well in From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3389 for alice@wonder.land; Mon, 29 Jul 2002 22:01:34 +0100 Date: Mon, 29 Jul 2002 22:01:29 +0100 From: Dormouse Message-Id: <200207292201.3389@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:36 2002 X-Envelope-Sender: dormouse@wonder.land They were learning to draw, and they drew all manner of things--everything that begins with an M-- From Alice Received: (from alice@wonder.land) by wonder.land id 3390 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:35 +0100 Date: Mon, 29 Jul 2002 22:01:30 +0100 From: Alice Message-Id: <200207292201.3390@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:37 2002 X-Envelope-Sender: alice@wonder.land Why with an M? From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3391 for alice@wonder.land; Mon, 29 Jul 2002 22:01:36 +0100 Date: Mon, 29 Jul 2002 22:01:31 +0100 From: Dormouse Message-Id: <200207292201.3391@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:38 2002 X-Envelope-Sender: dormouse@wonder.land Why not? From Dormouse Received: (from dormouse@wonder.land) by wonder.land id 3392 for alice@wonder.land; Mon, 29 Jul 2002 22:01:37 +0100 Date: Mon, 29 Jul 2002 22:01:32 +0100 From: Dormouse Message-Id: <200207292201.3392@wonder.land> To: Alice Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:39 2002 X-Envelope-Sender: dormouse@wonder.land --that begins with an M, such as mouse-traps, and the moon, and memory, and muchness-- you know you say things are "much of a muchness" --did you ever see such a thing as a drawing of a muchness? From Alice Received: (from alice@wonder.land) by wonder.land id 3393 for dormouse@wonder.land; Mon, 29 Jul 2002 22:01:38 +0100 Date: Mon, 29 Jul 2002 22:01:33 +0100 From: Alice Message-Id: <200207292201.3393@wonder.land> To: Dormouse Subject: Re: Story X-Envelope-Date: Mon Jul 29 22:01:40 2002 X-Envelope-Sender: alice@wonder.land Really, now you ask me, I don't think-- From Hatter Received: (from hatter@wonder.land) by wonder.land id 3394 for alice@wonder.land; Mon, 29 Jul 2002 22:01:39 +0100 Date: Mon, 29 Jul 2002 22:01:34 +0100 From: Hatter Message-Id: <200207292201.3394@wonder.land> To: Alice Subject: Rude remark X-Envelope-Date: Mon Jul 29 22:01:41 2002 X-Envelope-Sender: hatter@wonder.land Then you shouldn't talk From Alice Received: (from alice@wonder.land) by wonder.land id 3395 for alice@wonder.land; Mon, 29 Jul 2002 22:01:40 +0100 Date: Mon, 29 Jul 2002 22:01:35 +0100 From: Alice Message-Id: <200207292201.3395@wonder.land> To: Alice Subject: Thoughts X-Envelope-Date: Mon Jul 29 22:01:42 2002 X-Envelope-Sender: alice@wonder.land At any rate I'll never go THERE again! It's the stupidest tea-party I ever was at in all my life! gnatcoll-core-21.0.0/testsuite/tests/mmap/0000755000175000017500000000000013743647711020327 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/mmap/c_support.c0000644000175000017500000000147513661715457022522 0ustar nicolasnicolas#include #include #include #include /* We don't use standard lseek implementation on Windows otherwise * creating very large file will be inefficient as mingw lseek will * not create a sparse file. */ #if defined (_WIN32) || defined (__MINGW32__) #define LSEEK _lseeki64 #else #define LSEEK lseek #endif /* See test.adb */ int c_create_file (char* filename, int64_t offset) { int fd = creat (filename, 0644); if (fd < 0) { printf("cannot create file\n"); return 0; } if (LSEEK (fd, offset - 3, SEEK_SET) != offset - 3) { printf("cannot lseek\n"); close (fd); return 0; } if (write (fd, "xyz", 3) != 3) { printf("cannot write\n"); close (fd); return 0; } close (fd); return 1; } gnatcoll-core-21.0.0/testsuite/tests/mmap/test.adb0000644000175000017500000001034613661715457021764 0ustar nicolasnicolaswith Ada.Unchecked_Conversion; with GNAT.Directory_Operations; with GNAT.OS_Lib; with GNATCOLL.Mmap; with System; with Test_Assert; function Test return Integer is package A renames Test_Assert; package Mmap renames GNATCOLL.Mmap; use type Mmap.Mapped_File; use type Mmap.Mapped_Region; function Create_File (Filename : String; Size : Long_Long_Integer) return Boolean; -- Create a file of size Size filled with zeros excepts the last three -- characters that should contain the string "xyz". Return False if the -- file cannot be created on disk. Method is efficient even for large -- files as it create a sparse file. procedure Test_For_Size (Name : String; Size : Long_Long_Integer); -- Create a file of size Size called Name and fill it with 0 except last 3 -- characters that should contain "xyz". Ensure that we can read the file -- correctly using GNATcoll.Mmap facilities. ----------------- -- Create_File -- ----------------- function Create_File (Filename : String; Size : Long_Long_Integer) return Boolean is function C_Create_File (CFile : System.Address; Size : Long_Long_Integer) return Integer; -- internal C function. Return 0 in case of failure. pragma Import (C, C_Create_File, "c_create_file"); F : aliased String := Filename & ASCII.NUL; Status : Integer; begin Status := C_Create_File (F'Address, Size); if Status = 0 then return False; else return True; end if; end Create_File; ------------------- -- Test_For_Size -- ------------------- procedure Test_For_Size (Name : String; Size : Long_Long_Integer) is Fd : Mmap.Mapped_File; Mr : Mmap.Mapped_Region; Creation_Status : Boolean; Delete_Status : Boolean; Trace_Buffer : Mmap.Str_Access; begin Creation_Status := Create_File (Name, Size); A.Assert (Creation_Status, "Create test file of size" & Size'Img); Fd := Mmap.Open_Read (Name); A.Assert (Fd /= Mmap.Invalid_Mapped_File, "Open file"); GNATCOLL.Mmap.Read (File => Fd, Region => Mr, Offset => Mmap.File_Size (Size - 3), Length => 3, Mutable => False); A.Assert (Mr /= Mmap.Invalid_Mapped_Region, "valid region?"); A.Assert (Mmap.Is_Mmapped (Fd), "is mapped?"); Trace_Buffer := Mmap.Data (Mr); A.Assert (Trace_Buffer (1) = 'x', "expect character x"); A.Assert (Trace_Buffer (2) = 'y', "expect character y"); A.Assert (Trace_Buffer (3) = 'z', "expect character z"); A.Assert (Mmap.Last (Mr) = 3, "expect size 3, got" & Mmap.Last (Mr)'Img); -- Free resources GNATCOLL.Mmap.Free (Mr); GNATCOLL.Mmap.Close (Fd); GNAT.OS_Lib.Delete_File (Name, Delete_Status); A.Assert (Delete_Status); A.Assert (True, "end of test for size" & Size'Img); end Test_For_Size; begin -- close tests declare FD : Mmap.Mapped_File := Mmap.Invalid_Mapped_File; begin Mmap.Close (FD); A.Assert (True, "call to close on invalid mapped file should work"); exception when others => A.Assert (False, "unexpected exception on closing invalid mapped file"); end; -- tests on various file size Test_For_Size ("test1.txt", 8); Test_For_Size ("test2.txt", 1000); if GNAT.Directory_Operations.Dir_Separator = '\' or else Mmap.File_Size'Size > 32 then -- On windows 32bits and 64bits unixes test file length close to 4Go Test_For_Size ("test3.txt", 16#0_ffff_0000#); Test_For_Size ("test4.txt", 16#0_ffff_fffe#); Test_For_Size ("test5.txt", 16#0_ffff_ffff#); else -- On 32bits unixes the maximum offset is 2**31 (as lseek takes a -- signed long) Test_For_Size ("test3.txt", 16#0_0fff_0000#); Test_For_Size ("test4.txt", 16#0_0fff_fffe#); Test_For_Size ("test5.txt", 16#0_0fff_ffff#); end if; if Mmap.File_Size'Size > 32 then -- System supports large file > 4GB. Ensure that GNATcoll.Mmap does too Test_For_Size ("test6.txt", 16#1_0000_1000#); end if; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/mmap/test.yaml0000644000175000017500000000046713743647711022201 0ustar nicolasnicolasdescription: Test for GNATCOLL.MMAP control: - [SKIP, "disk_space() < 5000", "Not enough space disk to create temporary huge files"] - [XFAIL, "env.build.os.name == 'darwin' and env.build.os.version == '15.6.0'", "On older darwin versions the test might crash servers for unknown reason"] gnatcoll-core-21.0.0/testsuite/tests/projects/0000755000175000017500000000000013661715457021230 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/relocate_build_tree/0000755000175000017500000000000013661715457025224 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/relocate_build_tree/test.adb0000644000175000017500000000272213661715457026656 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Projects.Aux; with Test_Assert; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; begin Initialize (Env); PT.Load (GNATCOLL.VFS.Create ("p.gpr"), Env); Test_Assert.Assert (String (Build_Tree_Dir (Env.all)), "", "Wrong Build_Tree_Dir after load"); Test_Assert.Assert (String (Root_Dir (Env.all)), "", "Wrong Root_Dir after load"); Set_Build_Tree_Dir (Env.all, ".."); Test_Assert.Assert (String (Build_Tree_Dir (Env.all)), "..", "Wrong Build_Tree_Dir after Set_Build_Tree_Dir"); Test_Assert.Assert (String (Root_Dir (Env.all)), "", "Wrong Root_Dir after Set_Build_Tree_Dir"); Set_Root_Dir (Env.all, ".."); Test_Assert.Assert (String (Build_Tree_Dir (Env.all)), "..", "Wrong Build_Tree_Dir after Set_Root_Dir"); Test_Assert.Assert (String (Root_Dir (Env.all)), "..", "Wrong Root_Dir after Set_Root_Dir"); Set_Build_Tree_Dir (Env.all, ""); Set_Root_Dir (Env.all, ""); Test_Assert.Assert (String (Build_Tree_Dir (Env.all)), "", "Wrong Build_Tree_Dir after setting to default"); Test_Assert.Assert (String (Root_Dir (Env.all)), "", "Wrong Root_Dir after setting to default"); GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); PT.Unload; Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/relocate_build_tree/test.yaml0000644000175000017500000000013613661715457027067 0ustar nicolasnicolasdescription: > Check the getter/setter for Build_Tree_Dir and Root_Dir. data: - "p.gpr" gnatcoll-core-21.0.0/testsuite/tests/projects/relocate_build_tree/p.gpr0000644000175000017500000000002413661715457026171 0ustar nicolasnicolasproject P is end P; gnatcoll-core-21.0.0/testsuite/tests/projects/implicit_project/0000755000175000017500000000000013661715457024570 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/implicit_project/test.adb0000644000175000017500000000140313661715457026215 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Projects.Aux; with Test_Assert; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; Sources : File_Array_Access; begin Initialize (Env); GNATCOLL.Projects.Load_Implicit_Project (PT, Env); Sources := PT.Root_Project.Source_Files; Test_Assert.Assert (Sources'Length = 1, "check that sources are present"); Test_Assert.Assert (Sources (Sources.all'First).Display_Base_Name, "test.adb", "check source file name"); Unchecked_Free (Sources); GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); Unload (PT); Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/implicit_project/test.yaml0000644000175000017500000000011313661715457026426 0ustar nicolasnicolasdescription: Check that Load_Implicit_Project works. data: - "test.adb"gnatcoll-core-21.0.0/testsuite/tests/projects/no_language_duplicates/0000755000175000017500000000000013661715457025724 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/no_language_duplicates/test.adb0000644000175000017500000000160413661715457027354 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Projects.Aux; with Test_Assert; with GNAT.Strings; use GNAT.Strings; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; Langs : GNAT.Strings.String_List_Access; begin Initialize (Env); GNATCOLL.Projects.Load (PT, Root_Project_Path => Create ("t2.gpr"), Env => Env); Langs := new String_List'(PT.Root_Project.Languages (Recursive => True)); Test_Assert.Assert (Langs'Length = 2, "there must be two languages"); Test_Assert.Assert (Langs (1).all = "Ada", "check Ada casing"); Test_Assert.Assert (Langs (2).all = "C", "check C casing"); Free (Langs); GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); Unload (PT); Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/no_language_duplicates/t2.gpr0000644000175000017500000000015313661715457026762 0ustar nicolasnicolaswith "t1"; project T2 is for Languages use ("ada", "c"); for Source_Files use (); end T2; gnatcoll-core-21.0.0/testsuite/tests/projects/no_language_duplicates/test.yaml0000644000175000017500000000035113661715457027566 0ustar nicolasnicolasdescription: > Check that languages specified in different projects with different casing do not lead to duplicating entries in Languages query and resulting language names are normalized. data: - "t1.gpr" - "t2.gpr" gnatcoll-core-21.0.0/testsuite/tests/projects/no_language_duplicates/t1.gpr0000644000175000017500000000010013661715457026751 0ustar nicolasnicolasproject T1 is for Languages use ("Ada", "C"); end T1; gnatcoll-core-21.0.0/testsuite/tests/projects/reset_complex_attribute/0000755000175000017500000000000013661715457026164 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/reset_complex_attribute/test.adb0000644000175000017500000000215613661715457027617 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; with GNATCOLL.Projects.Aux; with Test_Assert; with Ada.Text_IO; use Ada.Text_IO; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; Dummy : Boolean; F : File_Type; begin Initialize (Env); PT.Load (GNATCOLL.VFS.Create ("p.gpr"), Env, Report_Missing_Dirs => False); PT.Root_Project.Set_Attribute (Obj_Dir_Attribute, "Bye"); PT.Root_Project.Set_Attribute (Exec_Dir_Attribute, "FooBar"); PT.Root_Project.Set_Modified (True); PT.Recompute_View; Dummy := PT.Root_Project.Save (Force => True); if not Dummy then raise Program_Error; end if; GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); PT.Unload; Free (Env); Open (F, In_File, "p.gpr"); Skip_Line (F, 2); Test_Assert.Assert (Get_Line (F), " for Object_Dir use ""Bye"";", "checking first edited line"); Test_Assert.Assert (Get_Line (F), " for Exec_Dir use ""FooBar"";", "checking second edited line"); Close (F); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/reset_complex_attribute/test.yaml0000644000175000017500000000024013661715457030023 0ustar nicolasnicolasdescription: > When replacing a composite attribute value make sure that no trailing parts of previous concatenation remain in place. data: - "p.gpr" gnatcoll-core-21.0.0/testsuite/tests/projects/reset_complex_attribute/p.gpr0000644000175000017500000000016313661715457027135 0ustar nicolasnicolasproject P is for Object_Dir use "Hello" & "/world"; for Exec_Dir use P'Object_Dir & "/exe"; end P; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/0000755000175000017500000000000013743647711025231 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/b.gpr0000644000175000017500000000015513661715457026167 0ustar nicolasnicolaswith "c.gpr"; project b is for Languages use ("Ada"); for Source_Files use ("src_b.ads"); end b; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/c.gpr0000644000175000017500000000012213661715457026162 0ustar nicolasnicolasproject c is for Languages use ("Ada"); for Source_Files use (); end c; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/src_d.ads0000644000175000017500000000006013661715457027012 0ustar nicolasnicolaspackage src_a is C_Var : Integer; end src_a; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/test.adb0000644000175000017500000000450613661715457026667 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Projects.Aux; with Test_Assert; use Test_Assert; function Test return Integer is Env : Project_Environment_Access; PT : GNATCOLL.Projects.Project_Tree; Aggregated : Project_Array_Access; begin Initialize (Env); PT.Load (GNATCOLL.VFS.Create (+"aggr.gpr"), Env); Aggregated := PT.Root_Project.Aggregated_Projects (Unwind_Aggregated => True); Test_Assert.Assert (Aggregated'Length = 4, "check list length Unwind_Aggregated = True"); declare Files : File_Array (1 .. 4); begin for I in 1 .. 4 loop Files (I) := Aggregated (I).Project_Path; end loop; Sort (Files); Assert (Files (1).Display_Base_Name, "a.gpr", "check 1st project"); Assert (Files (2).Display_Base_Name, "b.gpr", "check 2nd project"); Assert (Files (3).Display_Base_Name, "d.gpr", "check 3rd project"); Assert (Files (4).Display_Base_Name, "e.gpr", "check 4th project"); end; Unchecked_Free (Aggregated); Aggregated := PT.Root_Project.Aggregated_Projects (Unwind_Aggregated => False); Test_Assert.Assert (Aggregated'Length = 3, "check list length Unwind_Aggregated = False"); declare Files : File_Array (1 .. 3); begin for I in 1 .. 3 loop Files (I) := Aggregated (I).Project_Path; end loop; Sort (Files); Assert (Files (1).Display_Base_Name, "a.gpr", "check 1st project"); Assert (Files (2).Display_Base_Name, "b.gpr", "check 2nd project"); Assert (Files (3).Display_Base_Name, "nested_aggr.gpr", "check 3rd project"); end; Unchecked_Free (Aggregated); Aggregated := PT.Project_From_Name ("c").Aggregated_Projects; Test_Assert.Assert (Aggregated.all = Empty_Project_Array, "check empty list for non aggregate prpject"); Unchecked_Free (Aggregated); Test_Assert.Assert (PT.Project_From_Name ("abst").Is_Abstract_Project, "check that abstract project is abstract"); Test_Assert.Assert (not PT.Project_From_Name ("c").Is_Abstract_Project, "check that not abstract project is not abstract"); GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); PT.Unload; Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/nested_aggr.gpr0000644000175000017500000000020013661715457030217 0ustar nicolasnicolasaggregate project Nested_Aggr is for Project_Path use ("."); for Project_Files use ("e.gpr", "d.gpr"); end Nested_Aggr;gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/aggr.gpr0000644000175000017500000000042413661715457026665 0ustar nicolasnicolaswith "abst.gpr"; aggregate project Aggr is for Project_Path use ("."); for Project_Files use ("a.gpr", "b.gpr", "nested_aggr.gpr"); package Builder is for Global_Compilation_Switches ("Ada") use Abst.Comp_Switches; end Builder; end Aggr;gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/a.gpr0000644000175000017500000000015413661715457026165 0ustar nicolasnicolaswith "c.gpr"; project A is for Languages use ("Ada"); for Source_Files use ("src_a.ads"); end A; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/src_a.ads0000644000175000017500000000006013661715457027007 0ustar nicolasnicolaspackage src_a is C_Var : Integer; end src_a; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/abst.gpr0000644000175000017500000000010013661715457026665 0ustar nicolasnicolasabstract project abst is Comp_Switches := ("-O2"); end abst; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/e.gpr0000644000175000017500000000015513661715457026172 0ustar nicolasnicolaswith "c.gpr"; project e is for Languages use ("Ada"); for Source_Files use ("src_e.ads"); end e; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/src_e.ads0000644000175000017500000000006013661715457027013 0ustar nicolasnicolaspackage src_a is C_Var : Integer; end src_a; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/test.yaml0000644000175000017500000000053413743647711027076 0ustar nicolasnicolasdescription: > Check Aggregated_Projects and Is_Abstract_project. data: - "a.gpr" - "b.gpr" - "c.gpr" - "d.gpr" - "e.gpr" - "aggr.gpr" - "abst.gpr" - "nested_aggr.gpr" - "src_a.ads" - "src_b.ads" - "src_d.ads" - "src_e.ads" control: - [XFAIL, "env.valgrind", "Known memory leak: see S912-003"] gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/src_b.ads0000644000175000017500000000005613661715457027015 0ustar nicolasnicolaspackage src_b is C_Var : Float; end src_b; gnatcoll-core-21.0.0/testsuite/tests/projects/aggregated_projects/d.gpr0000644000175000017500000000015513661715457026171 0ustar nicolasnicolaswith "c.gpr"; project d is for Languages use ("Ada"); for Source_Files use ("src_d.ads"); end d; gnatcoll-core-21.0.0/testsuite/tests/projects/file_from_unit/0000755000175000017500000000000013661715457024231 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/file_from_unit/test1.gpr0000755000175000017500000000024213661715457026004 0ustar nicolasnicolasproject Test1 is package Naming is for Body ("proc") use "my__proc.txt"; for Body_Suffix ("ada") use ".ada"; end Naming; end Test1; gnatcoll-core-21.0.0/testsuite/tests/projects/file_from_unit/test.adb0000755000175000017500000000150413661715457025663 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with Test_Assert; function Test return Integer is Env : GNATCOLL.Projects.Project_Environment_Access; Tree : GNATCOLL.Projects.Project_Tree; begin Initialize (Env); Load (Tree, Env => Env, Root_Project_Path => Create (+"test1.gpr")); declare File_Name : constant String := +File_From_Unit (Project => Root_Project (Tree), Unit_Name => "PrOc", Part => Unit_Body, Language => "ada", File_Must_Exist => False); begin Test_Assert.Assert (File_Name, "my__proc.txt", "Wrong file name for unit Proc"); end; Tree.Unload; Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/file_from_unit/test.yaml0000755000175000017500000000015713661715457026102 0ustar nicolasnicolasdescription: File_From_Unit argument Unit name is case insensitive data: - "test1.gpr" - "my__proc.txt"gnatcoll-core-21.0.0/testsuite/tests/projects/file_from_unit/my__proc.txt0000755000175000017500000000040113661715457026577 0ustar nicolasnicolasprocedure Proc is function F (I : Integer) return Integer is begin return I + 1; end; function G (I : Integer) return Integer is (I + 1); procedure P is begin null; end; procedure Q is null; begin null; end; gnatcoll-core-21.0.0/testsuite/tests/projects/nested_external_references/0000755000175000017500000000000013661715457026615 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/nested_external_references/test.adb0000644000175000017500000000761613661715457030256 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with Test_Assert; with Ada.Containers.Indefinite_Ordered_Maps; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; package SV_Sets is new Ada.Containers.Indefinite_Ordered_Maps (String, Scenario_Variable); use SV_Sets; SV_Map : SV_Sets.Map; begin Initialize (Env); Env.Change_Environment ("A0", "a"); Env.Change_Environment ("B0", "b"); Env.Change_Environment ("C0", "c"); Env.Change_Environment ("NO_DEF", "no_def"); PT.Load (GNATCOLL.VFS.Create ("a.gpr"), Env); declare SVs : constant Scenario_Variable_Array := PT.Scenario_Variables; UVs : constant Untyped_Variable_Array := PT.Untyped_Variables; SV : Scenario_Variable; begin for SV of SVs loop Test_Assert.Assert (not SV_Map.Contains (External_Name (SV)), "Check no duplication of SV " & External_Name (SV)); SV_Map.Include (External_Name (SV), SV); end loop; -- Check simple case nesting. Test_Assert.Assert (SV_Map.Contains ("A0"), "Check presense of A0"); Test_Assert.Assert (SV_Map.Contains ("B0"), "Check presense of B0"); Test_Assert.Assert (SV_Map.Contains ("C0"), "Check presense of C0"); Test_Assert.Assert (SV_Map.Contains ("D0"), "Check presense of D0"); SV := SV_Map.Element ("A0"); Test_Assert.Assert (External_Default (SV), "b", "Check default of A0"); Test_Assert.Assert (Value (SV), "a", "Check value of A0"); SV := SV_Map.Element ("B0"); Test_Assert.Assert (External_Default (SV), "c", "Check default of B0"); Test_Assert.Assert (Value (SV), "b", "Check value of B0"); SV := SV_Map.Element ("C0"); Test_Assert.Assert (External_Default (SV), "d", "Check default of C0"); Test_Assert.Assert (Value (SV), "c", "Check value of C0"); SV := SV_Map.Element ("D0"); Test_Assert.Assert (External_Default (SV), "d", "Check default of D0"); Test_Assert.Assert (Value (SV), "d", "Check value of D0"); -- Check that duplicating ones before or after do not break -- the unwinding of nested external references. Test_Assert.Assert (SV_Map.Contains ("A1"), "Check presense of A1"); Test_Assert.Assert (SV_Map.Contains ("B1"), "Check presense of B1"); Test_Assert.Assert (SV_Map.Contains ("C1"), "Check presense of C1"); Test_Assert.Assert (SV_Map.Contains ("D1"), "Check presense of D1"); Test_Assert.Assert (SV_Map.Contains ("A2"), "Check presense of A2"); Test_Assert.Assert (SV_Map.Contains ("B2"), "Check presense of B2"); Test_Assert.Assert (SV_Map.Contains ("C2"), "Check presense of C2"); Test_Assert.Assert (SV_Map.Contains ("D2"), "Check presense of D2"); -- Check transition of duplicating SV with non-matching types into a UV -- and that it does not prevent unwinding further. Test_Assert.Assert (not SV_Map.Contains ("B3"), "Check absence of B3"); Test_Assert.Assert (SV_Map.Contains ("A3"), "Check presense of A3"); Test_Assert.Assert (SV_Map.Contains ("C3"), "Check presense of C3"); Test_Assert.Assert (SV_Map.Contains ("D3"), "Check presense of D3"); Test_Assert.Assert (UVs'Length > 0 and then External_Name (UVs (UVs'First)) = "B3", "Check transition of B3 from SVs to UVs"); -- Check that non-canonical nesting does not prevent collecting SVs -- declared prior to unexpected construct. Test_Assert.Assert (SV_Map.Contains ("A4"), "Check presense of A4"); Test_Assert.Assert (SV_Map.Contains ("B4"), "Check presense of B4"); SV_Map.Clear; end; PT.Unload; Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/nested_external_references/a.gpr0000644000175000017500000000231713661715457027552 0ustar nicolasnicolasproject A is -- Simple case type ABCD0 is ("a", "b", "c", "d"); VAR0 : ABCD0 := external ("A0", external ("B0", external ("C0", external ("D0", "d")))); -- Duplicating SVs before nesting: type ABCD1 is ("a", "b", "c", "d"); VAR1_A : ABCD1 := external ("A1", "a"); VAR1_B : ABCD1 := external ("B1", "b"); VAR1 : ABCD1 := external ("A1", external ("B1", external ("C1", external ("D1", "d")))); -- Duplicating SVs after nesting: type ABCD2 is ("a", "b", "c", "d"); VAR2 : ABCD2 := external ("A2", external ("B2", external ("C2", external ("D2", "d")))); VAR2_A : ABCD2 := external ("A2", "a"); VAR2_B : ABCD2 := external ("B2", "b"); -- Same external, different value sets: type ABCD3 is ("a", "b", "c", "d"); VAR3 : ABCD3 := external ("A3", external ("B3", external ("C3", external ("D3", "d")))); type E3 is ("e"); VAR3_B : E3 := external ("B3", "e"); -- Non-canonical nesting type ABCD4 is ("4a", "4b", "4c", "4d"); VAR4 : ABCD4 := external ("A4", external ("B4", "4" & external ("C4", external ("D4", "d")))); -- No default expression type No_Def is ("no_def"); No_Def_Var : No_Def := external ("NO_DEF"); end A; gnatcoll-core-21.0.0/testsuite/tests/projects/nested_external_references/test.yaml0000644000175000017500000000016413661715457030461 0ustar nicolasnicolasdescription: > Check support for treating nested external references as Scenario Variables. data: - "a.gpr" gnatcoll-core-21.0.0/testsuite/tests/projects/inconsistent_scenario_types/0000755000175000017500000000000013661715457027057 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/inconsistent_scenario_types/test.adb0000755000175000017500000000153613661715457030516 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Projects.Aux; with Test_Assert; function Test return Integer is Env : GNATCOLL.Projects.Project_Environment_Access; Tree : GNATCOLL.Projects.Project_Tree; begin Initialize (Env); Load (Tree, Root_Project_Path => Create (+"p.gpr"), Env => Env); declare SVs : constant Scenario_Variable_Array := Tree.Scenario_Variables; UVs : constant Untyped_Variable_Array := Tree.Untyped_Variables; begin Test_Assert.Assert (SVs'Length = 0, "check Scenario Variables amount"); Test_Assert.Assert (UVs'Length = 2, "check Untyped Variables amount"); end; GNATCOLL.Projects.Aux.Delete_All_Temp_Files (Tree.Root_Project); Tree.Unload; Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/inconsistent_scenario_types/q.gpr0000755000175000017500000000024113661715457030031 0ustar nicolasnicolasabstract project Q is type ValuesC is ("a", "b", "c", "d"); VarC : ValuesC := External ("Ext2", "a"); Boo := External ("Ext2", "e"); end Q; gnatcoll-core-21.0.0/testsuite/tests/projects/inconsistent_scenario_types/test.yaml0000755000175000017500000000043713661715457030731 0ustar nicolasnicolasdescription: > When scenario variables across the project tree are controlled by same external, but have different types with sets of values that do not match those are moved to untyped variables and a warning is passed to the Errors callback. data: - "p.gpr" - "q.gpr"gnatcoll-core-21.0.0/testsuite/tests/projects/inconsistent_scenario_types/p.gpr0000755000175000017500000000044413661715457030035 0ustar nicolasnicolaswith "q.gpr"; project P is type ValuesA is ("one", "two"); type ValuesB is ("two", "three"); VarA : ValuesA := External ("Ext1", "two"); VarB : ValuesB := External ("Ext1", "two"); type ValuesC is ("a", "b", "c"); VarC : ValuesC := External ("Ext2", "a"); end P; gnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value/0000755000175000017500000000000013661715457025752 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value/test.adb0000755000175000017500000000225713661715457027412 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with Test_Assert; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; begin Initialize (Env); GNATCOLL.Projects.Load (PT, Root_Project_Path => Create ("p.gpr"), Env => Env); declare UV_Array : constant Untyped_Variable_Array := PT.Untyped_Variables; UV : Untyped_Variable; Expected_Value : constant String := PT.Project_From_Name ("Q").Project_Path.Display_Dir_Name & "some_dir"; begin Test_Assert.Assert (UV_Array'Length = 2, "Wrong number of Untyped Variables"); if UV_Array'Length = 2 then UV := UV_Array (UV_Array'First); Test_Assert.Assert (External_Default (UV), Expected_Value, "Default external value is wrong for " & External_Name (UV)); UV := UV_Array (UV_Array'Last); Test_Assert.Assert (External_Default (UV), Expected_Value, "Default external value is wrong for " & External_Name (UV)); end if; end; PT.Unload; Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value/q.gpr0000755000175000017500000000035013661715457026725 0ustar nicolasnicolasproject Q is for Source_Files use (); Path := project'Project_Dir & "some_dir"; Var1 := external ("NONEXISTINGVAR1", Path); Var2 := external ("NONEXISTINGVAR2", project'Project_Dir & "some_dir"); end Q; gnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value/test.yaml0000755000175000017500000000014413661715457027617 0ustar nicolasnicolasdescription: Complex default value of external in imported project data: - "p.gpr" - "q.gpr"gnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value/p.gpr0000755000175000017500000000004313661715457026723 0ustar nicolasnicolaswith "q.gpr"; project P is end P; gnatcoll-core-21.0.0/testsuite/tests/projects/file_from_unit_separate/0000755000175000017500000000000013661715457026115 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/file_from_unit_separate/test.adb0000755000175000017500000000131613661715457027550 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with Test_Assert; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; U_Name : constant String := "Foo.Bar.Baz"; begin Initialize (Env); PT.Load (GNATCOLL.VFS.Create ("prj.gpr"), Env); Test_Assert.Assert (+GNATCOLL.Projects.File_From_Unit (Project => PT.Root_Project, Unit_Name => U_Name, Part => Unit_Separate, Language => "Ada", File_Must_Exist => False), "foo.bar.baz.3.ada", "check file name for separate"); PT.Unload; Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/file_from_unit_separate/test.yaml0000755000175000017500000000010513661715457027757 0ustar nicolasnicolasdescription: File_From_Unit works for separates data: - "prj.gpr"gnatcoll-core-21.0.0/testsuite/tests/projects/file_from_unit_separate/prj.gpr0000755000175000017500000000036413661715457027430 0ustar nicolasnicolasproject Prj is package Naming is for Dot_Replacement use "."; for Specification_Suffix ("ada") use ".1.ada"; for Implementation_Suffix ("ada") use ".2.ada"; for Separate_Suffix use ".3.ada"; end Naming; end Prj; gnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/0000755000175000017500000000000013661715457026321 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/src_q/0000755000175000017500000000000013661715457027430 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/src_q/a.ads0000644000175000017500000000002413661715457030335 0ustar nicolasnicolaspackage a is end a; gnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/test.adb0000644000175000017500000000232313661715457027750 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.Projects.Aux; with GNATCOLL.VFS; use GNATCOLL.VFS; with Test_Assert; with Ada.Containers.Indefinite_Ordered_Multisets; with Ada.Containers; use Ada.Containers; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; Files : File_Array_Access; package Sets is new Ada.Containers.Indefinite_Ordered_Multisets (String); use Sets; Set : Sets.Set; begin Make_Dir (Create ("obj_p")); Make_Dir (Create ("obj_q")); Make_Dir (Create ("obj_r")); Initialize (Env); PT.Load (GNATCOLL.VFS.Create ("p.gpr"), Env); Files := PT.Root_Project.Extended_Projects_Source_Files; for F of Files.all loop Set.Insert (F.Display_Base_Name); end loop; Unchecked_Free (Files); Test_Assert.Assert (Set.Length = 3, "check ammount of sources"); Test_Assert.Assert (Set.Contains ("a.ads"), "check source a.ads"); Test_Assert.Assert (Set.Contains ("b.ads"), "check source b.ads"); Test_Assert.Assert (Set.Contains ("c.ads"), "check source c.ads"); Set.Clear; GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); Unload (PT); Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/q.gpr0000644000175000017500000000015113661715457027270 0ustar nicolasnicolasproject q extends "r.gpr" is for Object_Dir use "obj_q"; for Source_Dirs use ("src_q"); end q; gnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/test.yaml0000644000175000017500000000025313661715457030164 0ustar nicolasnicolasdescription: > Check gathering sources of all projects extending a given one. data: - "p.gpr" - "q.gpr" - "r.gpr" - "src_p" - "src_q" - "src_r"gnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/src_r/0000755000175000017500000000000013661715457027431 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/src_r/c.ads0000644000175000017500000000002413661715457030340 0ustar nicolasnicolaspackage c is end c; gnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/src_r/b.ads0000644000175000017500000000002413661715457030337 0ustar nicolasnicolaspackage b is end b; gnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/src_r/a.ads0000644000175000017500000000002413661715457030336 0ustar nicolasnicolaspackage a is end a; gnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/p.gpr0000644000175000017500000000015113661715457027267 0ustar nicolasnicolasproject p extends "q.gpr" is for Object_Dir use "obj_p"; for Source_Dirs use ("src_p"); end p; gnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/r.gpr0000644000175000017500000000013113661715457027267 0ustar nicolasnicolasproject r is for Object_Dir use "obj_r"; for Source_Dirs use ("src_r"); end r; gnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/src_p/0000755000175000017500000000000013661715457027427 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/extended_project_sources/src_p/b.ads0000644000175000017500000000004413661715457030337 0ustar nicolasnicolaspackage B is B : Integer; end B; gnatcoll-core-21.0.0/testsuite/tests/projects/root_only_vars/0000755000175000017500000000000013661715457024307 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/root_only_vars/b.gpr0000755000175000017500000000031313661715457025242 0ustar nicolasnicolasabstract project B is type Var_B1_Type is ("VALUE_2", "VALUE_1"); Var_B1 : Var_B1_Type := external ("VAR_B1", "VALUE_1"); Var_B2 := External ("VAR_B2", "default"); end B; gnatcoll-core-21.0.0/testsuite/tests/projects/root_only_vars/test.adb0000644000175000017500000000553113661715457025742 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.Projects.Aux; with GNATCOLL.VFS; use GNATCOLL.VFS; with Test_Assert; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; begin Initialize (Env); PT.Load (GNATCOLL.VFS.Create ("a.gpr"), Env); declare SVs : constant Scenario_Variable_Array := PT.Scenario_Variables; UVs : constant Untyped_Variable_Array := PT.Untyped_Variables; begin Test_Assert.Assert (SVs'Length = 2, "checking initial quantity of SVs"); Test_Assert.Assert (UVs'Length = 2, "checking initial quantity of UVs"); end; declare SVs : constant Scenario_Variable_Array := PT.Scenario_Variables (True); UVs : constant Untyped_Variable_Array := PT.Untyped_Variables (True); begin Test_Assert.Assert (SVs'Length = 1, "checking root-only quantity of SVs"); Test_Assert.Assert (UVs'Length = 1, "checking root-only quantity of UVs"); end; declare SVs : constant Scenario_Variable_Array := PT.Scenario_Variables; UVs : constant Untyped_Variable_Array := PT.Untyped_Variables; begin Test_Assert.Assert (SVs'Length = 2, "checking quantity of SVs after root-only call"); Test_Assert.Assert (UVs'Length = 2, "checking quantity of UVs after root-only call"); end; declare SV1, SV2, SV3, SV4 : Scenario_Variable; UV1, UV2, UV3, UV4 : Untyped_Variable; begin SV1 := Scenario_Variables (PT, "VAR_A1", Root_Only => False); SV2 := Scenario_Variables (PT, "VAR_A1", Root_Only => True); SV3 := Scenario_Variables (PT, "VAR_B1", Root_Only => False); SV4 := Scenario_Variables (PT, "VAR_B1", Root_Only => True); UV1 := Get_Untyped_Variable (PT, "VAR_A2", Root_Only => False); UV2 := Get_Untyped_Variable (PT, "VAR_A2", Root_Only => True); UV3 := Get_Untyped_Variable (PT, "VAR_B2", Root_Only => False); UV4 := Get_Untyped_Variable (PT, "VAR_B2", Root_Only => True); Test_Assert.Assert (SV1 /= No_Variable, "Root SV recursive check"); Test_Assert.Assert (SV2 /= No_Variable, "Root SV non-recursive check"); Test_Assert.Assert (SV3 /= No_Variable, "Non-root SV recursive ckeck"); Test_Assert.Assert (SV4 = No_Variable, "Non-root SV non-recursive check"); Test_Assert.Assert (UV1 /= No_Untyped_Variable, "Root UV recursive check"); Test_Assert.Assert (UV2 /= No_Untyped_Variable, "Root UV non-recursive check"); Test_Assert.Assert (UV3 /= No_Untyped_Variable, "Non-root UV recursive ckeck"); Test_Assert.Assert (UV4 = No_Untyped_Variable, "Non-root UV non-recursive check"); end; GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); Unload (PT); Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/root_only_vars/a.gpr0000755000175000017500000000032113661715457025240 0ustar nicolasnicolaswith "b.gpr"; project A is type Var_A1_Type is ("VALUE_2", "VALUE_1"); Var_A1 : Var_A1_Type := external ("VAR_A1", "VALUE_1"); Var_A2 := External ("VAR_A2", "default"); end A; gnatcoll-core-21.0.0/testsuite/tests/projects/root_only_vars/test.yaml0000755000175000017500000000023313661715457026153 0ustar nicolasnicolasdescription: > Properly return the variables from root project only and not disturb the globally computed variables. data: - "a.gpr" - "b.gpr" gnatcoll-core-21.0.0/testsuite/tests/projects/inconsistent_var_names/0000755000175000017500000000000013661715457026003 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/inconsistent_var_names/test.adb0000644000175000017500000000317713661715457027442 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with Test_Assert; with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Containers.Indefinite_Ordered_Sets; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; procedure Errors (S : String); procedure Errors (S : String) is begin Test_Assert.Assert (Index (S, "VAR1") = 0 and Index (S, "VAR2") = 0 and Index (S, "VAR1_2") = 0, "check that SVs are not reported in "); end Errors; package UV_Sets is new Ada.Containers.Indefinite_Ordered_Sets (String); use UV_Sets; UV_Set : UV_Sets.Set; begin Initialize (Env); PT.Load (GNATCOLL.VFS.Create ("p.gpr"), Env, Errors => Errors'Unrestricted_Access); declare UVs : constant Untyped_Variable_Array := PT.Untyped_Variables; begin for UV of UVs loop Test_Assert.Assert (not UV_Set.Contains (External_Name (UV)), "check no duplication of UV " & External_Name (UV)); UV_Set.Include (External_Name (UV)); end loop; end; Test_Assert.Assert (UV_Set.Contains ("NESTED1"), "Check presense of NESTED1"); Test_Assert.Assert (UV_Set.Contains ("NESTED2"), "Check presense of NESTED2"); Test_Assert.Assert (not UV_Set.Contains ("VAR1"), "Check absence of VAR1"); Test_Assert.Assert (not UV_Set.Contains ("VAR2"), "Check absence of VAR2"); Test_Assert.Assert (not UV_Set.Contains ("VAR1_2"), "Check absence of VAR1_2"); UV_Set.Clear; PT.Unload; Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/inconsistent_var_names/test.yaml0000644000175000017500000000034013661715457027643 0ustar nicolasnicolasdescription: > When two scenario variable have inconsistent set of possible values and same controlling external reference is a nested one the proper name of the nested external must be reported. data: - "p.gpr" gnatcoll-core-21.0.0/testsuite/tests/projects/inconsistent_var_names/p.gpr0000644000175000017500000000104513661715457026754 0ustar nicolasnicolasproject P is type T_1_1 is ("a", "b"); V_P_1_1 : T_1_1 := external ("VAR1", external ("NESTED1", "a")); type T_1_2 is ("a", "b", "c"); V_P_1_2 : T_1_2 := external ("NESTED1", "a"); V_P_1_3 : T_1_2 := external ("VAR1_2", external ("NESTED1", "a")); type T_2_1 is ("a", "b"); V_P_2_1 : T_2_1 := external ("NESTED2", "a"); type T_2_2 is ("a", "b", "c"); V_P_2_2 : T_2_2 := external ("VAR2", external ("NESTED2", "a")); V_P_2_3 : T_2_2 := external ("NESTED2", "a"); for source_dirs use (); end P; gnatcoll-core-21.0.0/testsuite/tests/projects/source_files_sort/0000755000175000017500000000000013661715457024761 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/source_files_sort/test.adb0000644000175000017500000000153513661715457026414 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Projects.Aux; with Test_Assert; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; Sources : File_Array_Access; begin Initialize (Env); GNATCOLL.Projects.Load (PT, Root_Project_Path => Create ("foo.gpr"), Env => Env); Sources := PT.Root_Project.Source_Files (Recursive => True); Test_Assert.Assert (Sources'Length = 2, "check ammount of sources"); Test_Assert.Assert (Sources (1).Display_Full_Name < Sources (2).Display_Full_Name, "check correct order of sources"); Unchecked_Free (Sources); GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); Unload (PT); Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/source_files_sort/test.yaml0000644000175000017500000000017113661715457026623 0ustar nicolasnicolasdescription: Source_Files result sorted alphabetically data: - "foo.gpr" - "foo1.gpr" - "foo_a" - "foo_b"gnatcoll-core-21.0.0/testsuite/tests/projects/source_files_sort/foo.gpr0000644000175000017500000000011013661715457026246 0ustar nicolasnicolaswith "foo1"; project foo is for Source_Dirs use ("foo_a"); end foo; gnatcoll-core-21.0.0/testsuite/tests/projects/source_files_sort/foo_b/0000755000175000017500000000000013661715457026045 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/source_files_sort/foo_b/p.ads0000644000175000017500000000002413661715457026771 0ustar nicolasnicolaspackage P is end p; gnatcoll-core-21.0.0/testsuite/tests/projects/source_files_sort/foo1.gpr0000644000175000017500000000007413661715457026340 0ustar nicolasnicolasproject foo1 is for Source_Dirs use ("foo_b"); end foo1; gnatcoll-core-21.0.0/testsuite/tests/projects/source_files_sort/foo_a/0000755000175000017500000000000013661715457026044 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/source_files_sort/foo_a/q.ads0000644000175000017500000000002413661715457026771 0ustar nicolasnicolaspackage q is end q; gnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value_agg/0000755000175000017500000000000013743647711026566 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value_agg/agg.gpr0000644000175000017500000000010613661715457030035 0ustar nicolasnicolasaggregate project agg is for Project_Files use ("p.gpr"); end agg; gnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value_agg/test.adb0000644000175000017500000000126513661715457030223 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Projects.Aux; with Test_Assert; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; UV : Untyped_Variable; begin Initialize (Env); PT.Load (GNATCOLL.VFS.Create ("agg.gpr"), Env); UV := PT.Get_Untyped_Variable ("VAL"); Test_Assert.Assert (UV /= No_Untyped_Variable, "check that variable is detected"); Test_Assert.Assert (External_Default (UV), "val", "check external default value"); GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); PT.Unload; Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value_agg/abst.gpr0000644000175000017500000000014113661715457030227 0ustar nicolasnicolasabstract project abst is Val := "val"; Architecture := external ("VAL", Val); end abst; gnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value_agg/test.yaml0000644000175000017500000000043113743647711030427 0ustar nicolasnicolasdescription: > Default value of external reference in an aggregated project is a reference of another variable declared in same aggregated subtree. data: - "p.gpr" - "agg.gpr" - "abst.gpr" control: - [XFAIL, "env.valgrind", "Known memory leak: see S912-005"] gnatcoll-core-21.0.0/testsuite/tests/projects/external_default_value_agg/p.gpr0000644000175000017500000000010613661715457027536 0ustar nicolasnicolaswith "abst.gpr"; project p is for Source_Dirs use ("."); end p; gnatcoll-core-21.0.0/testsuite/tests/projects/type_from_missing_with/0000755000175000017500000000000013743647711026016 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/type_from_missing_with/test.adb0000755000175000017500000000321313743647711027447 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with Ada.Strings.Fixed; use Ada.Strings.Fixed; with GNATCOLL.Projects.Aux; with Ada.Containers.Indefinite_Doubly_Linked_Lists; with Test_Assert; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; package String_Lists is new Ada.Containers.Indefinite_Doubly_Linked_Lists (String); use String_Lists; Lines : String_Lists.List; Cur : String_Lists.Cursor; procedure Errors (S : String); procedure Errors (S : String) is begin Lines.Append (S); end Errors; Load_Failed : Boolean := False; begin Initialize (Env); begin GNATCOLL.Projects.Load (PT, Root_Project_Path => Create_From_Base ("p.gpr"), Env => Env, Recompute_View => False, Errors => Errors'Unrestricted_Access); exception when Invalid_Project => Load_Failed := True; end; Test_Assert.Assert (Load_Failed, "check that load failed"); Cur := Lines.First; Test_Assert.Assert (Index (Element (Cur), "p.gpr:1:06: imported project file ""Common1.gpr""") /= 0, "check first error line"); Next (Cur); Test_Assert.Assert (Index (Element (Cur), "p.gpr:3:10: unknown project ""common""") /= 0, "check second error line"); Next (Cur); Test_Assert.Assert (Cur = No_Element, "check that there are no more error lines"); Lines.Clear; GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); PT.Unload; Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/type_from_missing_with/test.yaml0000644000175000017500000000033513743647711027662 0ustar nicolasnicolasdescription: > Fail gracefully when variable type is form an unknown project and there are missing withed projects. data: - "p.gpr" control: - [XFAIL, "env.valgrind", "Known memory leak: see S912-005"] gnatcoll-core-21.0.0/testsuite/tests/projects/type_from_missing_with/p.gpr0000755000175000017500000000013513661715457026773 0ustar nicolasnicolaswith "Common1.gpr"; project P is Doo : Common.T := external ("WHATEVER", "a"); end P; gnatcoll-core-21.0.0/testsuite/tests/projects/main_unit_path/0000755000175000017500000000000013661715457024227 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/main_unit_path/test.adb0000644000175000017500000000255213661715457025662 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Projects.Aux; with Test_Assert; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; Dummy : Boolean; VF_Full_Good : constant Virtual_File := Get_Current_Dir / "src" / "foo.adb"; VF_Full_Bad : constant Virtual_File := Get_Current_Dir / "not_src" / "foo.adb"; VF_Relative : constant Virtual_File := Create ("boo") / "foo.adb"; begin Make_Dir (Create ("src")); Make_Dir (Create ("not_src")); Copy (Create ("foo.adb"), "src", Dummy); Copy (Create ("foo.adb"), "not_src", Dummy); Initialize (Env); GNATCOLL.Projects.Load (PT, Root_Project_Path => Create ("foo1.gpr"), Env => Env); Test_Assert.Assert (PT.Root_Project.Is_Main_File ("foo.adb"), "testing base name"); Test_Assert.Assert (PT.Root_Project.Is_Main_File (VF_Full_Good.Full_Name), "testing full correct path"); Test_Assert.Assert (not PT.Root_Project.Is_Main_File (VF_Full_Bad.Full_Name), "testing full incorrect path"); Test_Assert.Assert (PT.Root_Project.Is_Main_File (VF_Relative.Full_Name), "testing relative path"); GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); Unload (PT); Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/main_unit_path/foo.adb0000644000175000017500000000005113661715457025456 0ustar nicolasnicolasprocedure Foo is begin null; end Foo; gnatcoll-core-21.0.0/testsuite/tests/projects/main_unit_path/test.yaml0000644000175000017500000000020613661715457026070 0ustar nicolasnicolasdescription: > Check that Is_Main_Unit correctly handles absolute and relative path names. data: - "foo1.gpr" - "foo.adb" gnatcoll-core-21.0.0/testsuite/tests/projects/main_unit_path/foo1.gpr0000644000175000017500000000012713661715457025605 0ustar nicolasnicolasproject foo1 is for Source_Dirs use ("src"); for Main use ("foo.adb"); end foo1; gnatcoll-core-21.0.0/testsuite/tests/projects/c_cpp_extensions/0000755000175000017500000000000013661715457024573 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/projects/c_cpp_extensions/test.adb0000644000175000017500000000265513661715457026232 0ustar nicolasnicolaswith GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Projects.Aux; with Test_Assert; function Test return Integer is PT : Project_Tree; Env : Project_Environment_Access; begin Initialize (Env); GNATCOLL.Projects.Load (PT, Root_Project_Path => Create ("p.gpr"), Env => Env); declare Body_Ext : constant String := PT.Root_Project.Attribute_Value (Attribute => Impl_Suffix_Attribute, Index => "c"); Spec_Ext : constant String := PT.Root_Project.Attribute_Value (Attribute => Spec_Suffix_Attribute, Index => "c"); begin Test_Assert.Assert (Spec_Ext, ".h", "check C spec extension"); Test_Assert.Assert (Body_Ext, ".c", "check C body extension"); end; declare Body_Ext : constant String := PT.Root_Project.Attribute_Value (Attribute => Impl_Suffix_Attribute, Index => "c++"); Spec_Ext : constant String := PT.Root_Project.Attribute_Value (Attribute => Spec_Suffix_Attribute, Index => "c++"); begin Test_Assert.Assert (Spec_Ext, ".hh", "check C++ spec extension"); Test_Assert.Assert (Body_Ext, ".cpp", "check C++ body extension"); end; GNATCOLL.Projects.Aux.Delete_All_Temp_Files (PT.Root_Project); Unload (PT); Free (Env); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/projects/c_cpp_extensions/test.yaml0000644000175000017500000000025513661715457026440 0ustar nicolasnicolasdescription: > Check default extensions for C and C++ in case when autoconf is not enabled and naming scheme comes from predefined in-memory cgpr. data: - "p.gpr" gnatcoll-core-21.0.0/testsuite/tests/projects/c_cpp_extensions/p.gpr0000644000175000017500000000007713661715457025550 0ustar nicolasnicolasproject p is for Languages use ("ada", "c", "c++"); end p; gnatcoll-core-21.0.0/testsuite/tests/geometry/0000755000175000017500000000000013661715457021232 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/geometry/test.adb0000644000175000017500000004250013661715457022662 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Text_IO; with GNATCOLL.Geometry; with Test_Assert; function Test return Integer is package IO renames Ada.Text_IO; package A renames Test_Assert; generic type Coordinate is digits <>; with package G is new GNATCOLL.Geometry (Coordinate); package Asserts is use type G.Point; Tolerance : constant G.Distance_Type := 1.0E-5; -- Tolerance when comparing distances procedure Put (P : G.Point); -- Put image of a point on stdout procedure Put (C : G.Circle); -- Put image of a circle on stdout procedure Assert (P1, P2 : G.Point; Error : String); -- Check if two points are equals procedure Assert_Inside (P : G.Point; S : G.Segment; On_Line : Boolean := True; On_Segment : Boolean := True; Error : String := ""); procedure Assert_Inside_Triangle (P : G.Point; T : G.Triangle; Result : Boolean := True; Error : String := ""); procedure Assert_Intersection (S1, S2 : G.Segment; On_Line, On_Segment : G.Point; Error : String); procedure Assert_Intersect_Triangle (T1, T2 : G.Triangle; Result : Boolean; Error : String); procedure Assert_Intersect_Rectangle (R1, R2 : G.Rectangle; Result : Boolean; Error : String); procedure Assert_Same_Side (P1, P2 : G.Point; S : G.Segment; Result : Boolean; Error : String); procedure Assert (D1, D2 : Coordinate'Base; Error : String); procedure Assert_Distance (P : G.Point; S : G.Segment; On_Line, On_Segment : Coordinate; Error : String); procedure Assert_Area (P : G.Polygon; D : Coordinate; Error : String); procedure Assert (C1, C2 : G.Circle; Error : String); end Asserts; package body Asserts is --------- -- Put -- --------- procedure Put (P : G.Point) is begin if P = G.No_Point then IO.Put ("(No point)"); elsif P = G.Infinity_Points then IO.Put ("(Infinity of points)"); else IO.Put ("(" & P.X'Img & "," & P.Y'Img & ")"); end if; end Put; --------- -- Put -- --------- procedure Put (C : G.Circle) is begin IO.Put ("(c="); Put (C.Center); IO.Put (" r=" & C.Radius'Img); end Put; ------------ -- Assert -- ------------ procedure Assert (P1, P2 : G.Point; Error : String) is Success : constant Boolean := P1 = P2; begin A.Assert (Success, Error); if not Success then IO.Put ("expected: "); Put (P1); IO.New_Line; IO.Put ("got: "); Put (P2); IO.New_Line; end if; end Assert; procedure Assert_Inside (P : G.Point; S : G.Segment; On_Line : Boolean := True; On_Segment : Boolean := True; Error : String := "") is SR : constant G.Segment := (S (2), S (1)); begin A.Assert (G.Inside (P, G.To_Line (S)) = On_Line, Error & " (line test)"); A.Assert (G.Inside (P, G.To_Line (SR)) = On_Line, Error & " (reverse line test)"); A.Assert (G.Inside (P, S) = On_Segment, Error & " (segment test)"); A.Assert (G.Inside (P, SR) = On_Segment, Error & " (reverse segment test)"); end Assert_Inside; procedure Assert_Inside_Triangle (P : G.Point; T : G.Triangle; Result : Boolean := True; Error : String := "") is T2 : constant G.Triangle := (T (3), T (2), T (1)); begin A.Assert (G.Inside (P, T) = Result, Error & " (triangle test)"); A.Assert (G.Inside (P, T2) = Result, Error & " (reverse triangle test)"); A.Assert (G.Inside (P, G.Polygon (T)) = Result, Error & " (polygon test)"); A.Assert (G.Inside (P, G.Polygon (T2)) = Result, Error & " (reverse polygon test)"); end Assert_Inside_Triangle; procedure Assert_Intersection (S1, S2 : G.Segment; On_Line : G.Point; On_Segment : G.Point; Error : String) is begin A.Assert (G.Intersection (G.To_Line (S1), G.To_Line (S2)) = On_Line, Error & " (line test)"); A.Assert (G.Intersection (G.To_Line (S2), G.To_Line (S1)) = On_Line, Error & " (reverse line test)"); A.Assert (G.Intersection (S1, S2) = On_Segment, Error & " (segment test)"); A.Assert (G.Intersection (S2, S1) = On_Segment, Error & " (reverse segment test)"); end Assert_Intersection; procedure Assert_Intersect_Triangle (T1, T2 : G.Triangle; Result : Boolean; Error : String) is T3 : constant G.Triangle := (T1 (2), T1 (3), T1 (1)); begin A.Assert (G.Intersect (T1, T2) = Result, Error & " (test1)"); A.Assert (G.Intersect (T2, T1) = Result, Error & " (test2)"); A.Assert (G.Intersect (T3, T2) = Result, Error & " (test3)"); A.Assert (G.Intersect (T2, T3) = Result, Error & " (test4)"); end Assert_Intersect_Triangle; procedure Assert_Intersect_Rectangle (R1, R2 : G.Rectangle; Result : Boolean; Error : String) is begin A.Assert (G.Intersect (R1, R2) = Result, Error); end Assert_Intersect_Rectangle; procedure Assert_Same_Side (P1, P2 : G.Point; S : G.Segment; Result : Boolean; Error : String) is begin A.Assert (G.Same_Side (P1, P2, S) = Result, Error & " (segment test)"); A.Assert (G.Same_Side (P1, P2, G.To_Line (S)) = Result, Error & " (line test)"); exception when others => A.Assert (False, Error & " (exception raised)"); end Assert_Same_Side; procedure Assert (D1, D2 : Coordinate'Base; Error : String) is Success : constant Boolean := abs (D1 - D2) <= Tolerance; begin A.Assert (Success, Error); if not Success then IO.Put_Line ("expected: " & D1'Img); IO.Put_Line ("got: " & D2'Img); end if; end Assert; procedure Assert_Distance (P : G.Point; S : G.Segment; On_Line, On_Segment : Coordinate; Error : String) is SR : constant G.Segment := (S (2), S (1)); begin A.Assert (G.Distance (P, G.To_Line (S)) = On_Line, Error & " (line test)"); A.Assert (G.Distance (P, G.To_Line (SR)) = On_Line, Error & " (reverse line test)"); A.Assert (G.Distance (P, S) = On_Segment, Error & " (segment test)"); A.Assert (G.Distance (P, SR) = On_Segment, Error & " (reverse segment test)"); end Assert_Distance; procedure Assert_Area (P : G.Polygon; D : Coordinate; Error : String) is begin Assert (G.Area (P), abs (D), Error & " (poly test)"); if P'Length = 3 then Assert (G.Area (G.Triangle (P)), D, Error & " (triangle test)"); end if; end Assert_Area; procedure Assert (C1, C2 : G.Circle; Error : String) is use type G.Circle; Success : constant Boolean := C1 = C2; begin A.Assert (Success, Error); if not Success then IO.Put ("expected: "); Put (C1); IO.New_Line; IO.Put ("got: "); Put (C2); IO.New_Line; end if; end Assert; end Asserts; generic type Coordinate is digits <>; procedure Tests; procedure Tests is package G is new GNATCOLL.Geometry (Coordinate); package Assertions is new Asserts (Coordinate, G); use Assertions, G, G.Coordinate_Elementary_Functions; begin Assert_Inside ((2.0, 0.0), ((0.0, 0.0), (10.0, 0.0)), True, True, "Point on vertical line"); Assert_Inside ((-1.0, 0.0), ((0.0, 0.0), (10.0, 0.0)), True, False, "Point on vertical line, but not on segment"); Assert_Inside ((0.0, 0.0), ((0.0, 0.0), (10.0, 0.0)), True, True, "Point on vertical line at one end"); Assert_Inside ((0.0, 2.0), ((0.0, 0.0), (0.0, 10.0)), True, True, "Point on horizontal line"); Assert_Inside ((0.0, -1.0), ((0.0, 0.0), (0.0, 10.0)), True, False, "Point on horizontal line, but not on segment"); Assert_Inside ((0.0, 0.0), ((0.0, 0.0), (0.0, 10.0)), True, True, "Point on horizontal line at one end"); Assert_Inside ((0.0, 0.0), ((-2.0, 2.0), (2.0, 2.0)), False, False, "Not on line nor segment"); Assert_Intersection (((0.0, 0.0), (10.0, 0.0)), ((0.0, 0.0), (10.0, 0.0)), Infinity_Points, Infinity_Points, "Intersection of same horizontal line is infinite"); Assert_Intersection (((0.0, 0.0), (10.0, 0.0)), ((0.0, 0.0), (5.0, 0.0)), Infinity_Points, Infinity_Points, "Intersection of overlapping horizontal line is infinite"); Assert_Intersection (((0.0, 0.0), (0.0, 10.0)), ((0.0, 0.0), (0.0, 10.0)), Infinity_Points, Infinity_Points, "Intersection of same vertical line is infinite"); Assert_Intersection (((0.0, 0.0), (0.0, 10.0)), ((0.0, 0.0), (0.0, 5.0)), Infinity_Points, Infinity_Points, "Intersection of overlapping vertical vectors is infinite"); Assert_Intersection (((0.0, 0.0), (10.0, 0.0)), ((-2.0, 2.0), (2.0, 2.0)), No_Point, No_Point, "No intersection of parallel vectors"); Assert_Intersection (((0.0, 0.0), (10.0, 0.0)), ((11.0, 0.0), (20.0, 0.0)), Infinity_Points, No_Point, "Intersection of aligned vectors"); Assert_Intersection (((0.0, 0.0), (0.0, 10.0)), ((-2.0, 2.0), (2.0, 2.0)), Point'(0.0, 2.0), Point'(0.0, 2.0), "Simple segment intersection"); Assert_Intersection (((0.0, 0.0), (0.0, 10.0)), ((-2.0, 0.0), (2.0, 0.0)), Point'(0.0, 0.0), Point'(0.0, 0.0), "Intersection at one end of the vectors"); Assert_Intersection (((0.0, 0.0), (10.0, 0.0)), ((-2.0, 2.0), (2.0, 2.0)), No_Point, No_Point, "Parallel lines have no intersection"); Assert_Distance ((0.0, 0.0), ((-2.0, 2.0), (2.0, 2.0)), 2.0, 2.0, "Simple distance to line"); Assert_Distance ((0.0, 2.0), ((-2.0, 2.0), (2.0, 2.0)), 0.0, 0.0, "Distance when point on line"); Assert_Distance ((-3.0, 2.0), ((-2.0, 2.0), (2.0, 2.0)), 0.0, 1.0, "Distance when point on line, not on segment"); Assert_Distance ((-3.0, 0.0), ((-2.0, 2.0), (2.0, 2.0)), 2.0, Sqrt (5.0), "Distance when orthogonal projection not on segment"); Assert_Area (((0.0, 0.0), (4.0, 0.0), (0.0, 3.0)), 6.0, "Area of a counter-clockwise triangle"); Assert_Area (((0.0, 0.0), (0.0, 3.0), (4.0, 0.0)), 6.0, "Area of clockwise triangle"); Assert_Area (((0.0, 0.0), (0.0, 3.0), (2.0, 3.0), (2.0, 4.0), (3.0, 4.0), (3.0, 0.0)), 10.0, "Area for two squares (non-convex)"); Assert (To_Circle ((0.0, 0.0), (1.0, 0.0), (0.0, 1.0)), ((0.5, 0.5), Sqrt (0.5)), "Circle from point"); Assert_Inside_Triangle ((0.0, 0.0), ((0.0, 0.0), (0.0, 1.0), (1.0, 0.0)), True, "Point on boundary of triangle"); Assert_Inside_Triangle ((0.1, 0.1), ((0.0, 0.0), (0.0, 1.0), (1.0, 0.0)), True, "Point inside triangle"); Assert_Inside_Triangle ((0.5, 1.0), ((0.0, 0.0), (0.0, 1.0), (1.0, 0.0)), False, "Point outside triangle"); A.Assert (G.Inside ((2.5, 3.5), Poly => ((0.0, 0.0), (0.0, 3.0), (2.0, 3.0), (2.0, 4.0), (3.0, 4.0), (3.0, 0.0))), "Point inside non-convex polygon"); A.Assert (G.Inside ((2.0, 2.5), Poly => ((0.0, 0.0), (0.0, 3.0), (2.0, 3.0), (2.0, 4.0), (3.0, 4.0), (3.0, 0.0))), "Point inside non-convex polygon, vertically below vertex"); Assert_Same_Side ((0.0, 0.0), (4.0, 0.0), ((-2.0, 2.0), (2.0, 2.0)), True, "Same side of horizontal line"); Assert_Same_Side ((0.0, 0.0), (0.0, 4.0), ((-2.0, 2.0), (-2.0, -2.0)), True, "Same side of vertical line"); Assert_Same_Side ((0.0, 0.0), (4.0, 0.0), ((2.0, 2.0), (2.0, -2.0)), False, "Not same side of vertical line"); Assert (Centroid (((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0))), (0.5, 0.5), "Centroid of rectangle"); Assert (Centroid (((0.0, 0.0), (1.0, 0.0), (0.0, 1.0))), (1.0 / 3.0, 1.0 / 3.0), "Centroid of triangle"); Assert_Intersect_Triangle (((0.0, 0.0), (1.0, 0.0), (0.0, 1.0)), ((0.5, 0.0), (1.5, 0.0), (0.5, 1.0)), True, "Intersection of two triangles"); Assert_Intersect_Triangle (((0.0, 0.0), (1.0, 0.0), (0.0, 1.0)), ((1.0, 0.0), (2.0, 0.0), (1.0, 1.0)), True, "Intersection with one common vertex"); Assert_Intersect_Triangle (((0.0, 0.0), (1.0, 0.0), (0.0, 1.0)), ((1.1, 0.0), (2.1, 0.0), (1.1, 1.0)), False, "No Intersection"); Assert_Intersect_Rectangle (((0.0, 0.0), (1.0, 1.0)), ((0.5, 0.0), (1.5, 2.0)), True, "Intersection of rectangles"); Assert_Intersect_Rectangle (((0.0, 0.0), (1.0, 1.0)), ((0.5, 1.0), (1.5, 2.0)), True, "Intersection of rectangles with common edge"); Assert_Intersect_Rectangle (((0.0, 0.0), (1.0, 1.0)), ((0.5, 1.1), (1.5, 2.0)), False, "No intersection of rectangles"); -- From the documentation of Boost geometry -- http://geometrylibrary.geodan.nl/ declare P1 : constant Point := (1.0, 1.0); P2 : constant Point := (2.0, 3.0); P3 : constant Point := (3.7, 2.0); P : constant Polygon := ((2.0, 1.3), (4.1, 3.0), (5.3, 2.6), (2.9, 0.7)); begin Assert (2.23607, Distance (P1, P2), "Distance P1-P2"); Assert (3.015, Area (P), "Area of polygon"); A.Assert (Inside (P3, P), "Point inside polygon"); Assert (1.04403, Distance (P1, P), "Distance point-polygon"); end; end Tests; begin IO.Put_Line ("==== Coordinates = Float ===="); declare subtype Coordinate is Float; procedure Float_Tests is new Tests (Coordinate); begin Float_Tests; end; -- Check we can instantiate with more restricted types IO.Put_Line ("==== Coordinates = -500.0 .. 500.0 ===="); declare type Coordinate is digits 1 range -500.0 .. 500.0; procedure Float_Tests is new Tests (Coordinate); begin Float_Tests; end; IO.Put_Line ("==== Coordinates = -500.0 .. 200.0 ===="); declare type Coordinate is digits 1 range -500.0 .. -200.0; package Geom is new GNATCOLL.Geometry (Coordinate); package Assertions is new Asserts (Coordinate, Geom); use Geom, Assertions; P1 : constant Point := (-401.0, -401.0); P2 : constant Point := (-402.0, -403.0); begin Assert (2.23607, Distance (P1, P2), "Distance P1-P2"); end; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/geometry/test.yaml0000644000175000017500000000005013661715457023070 0ustar nicolasnicolasdescription: Test for GNATCOLL.Geometry gnatcoll-core-21.0.0/testsuite/tests/arg_lists/0000755000175000017500000000000013661715457021366 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/arg_lists/test.adb0000644000175000017500000003562213661715457023025 0ustar nicolasnicolaswith Ada.Strings.Unbounded; with GNAT.Source_Info; with GNAT.Strings; with GNATCOLL.Arg_Lists; use GNATCOLL.Arg_Lists; with Test_Assert; function Test return Integer is package A renames Test_Assert; package SI renames GNAT.Source_Info; type String_Array is array (Positive range <>) of Ada.Strings.Unbounded.Unbounded_String; function "+" (S : String) return Ada.Strings.Unbounded.Unbounded_String renames Ada.Strings.Unbounded.To_Unbounded_String; function "+" (S : Ada.Strings.Unbounded.Unbounded_String) return String renames Ada.Strings.Unbounded.To_String; function Create_Args (Command : String; Args : String_Array; Cmd_Mode : Command_Line_Mode; Arg_Mode : Argument_Mode) return Arg_List; -- Synthetic constructor for Arg_List procedure Test_Parse_String (Debug_String, Command, Text : String; Location : String := SI.Source_Location); -- Run Parse_String on Command and Text and check that we get the given -- debug string. procedure Test_Parse_String (Debug_String, Text : String; Mode : Command_Line_Mode; Location : String := SI.Source_Location); -- Run Parse_String on Text and Mode and check that we get the given -- debug string. procedure Test_Argument_List_To_String (Expected : String; List : String_Array; Protect_Quotes : Boolean); -- Run Argument_List_To_String on List and Protect_Quotes and check that it -- returns Expected. procedure Test_To_List (Expected : String_Array; C : String_Array; Include_Command : Boolean; Location : String := SI.Source_Location); -- Run To_List on C and Include_Command and check that it returns an -- Argument_List corresponding to Expected. function Substitution_Callback (Param : String; Mode : Command_Line_Mode) return Arg_List; -- Callback for our Substitution tests procedure Test_Substitute (Debug_String : String; Command : String := "cmd"; Args : String_Array; Cmd_Mode : Command_Line_Mode; Arg_Mode : Argument_Mode; Location : String := SI.Source_Location); -- Create a command-line with the given Cmd_Mode and Command as the -- command. Append all arguments from Args with the given Arg_Mode. Then, -- run Substitute on this command-line with the '%' substitution character -- and Substitution_Callback. procedure Test_To_Display_String (Expected : String; C : Arg_List; Include_Command : Boolean; Max_Arg_Length : Positive := Positive'Last); -- Check that To_Display_String (C, Include_Command, Max_Arg_Length) -- returns Expected. procedure Test_To_Script_String (Expected : String; C : Arg_List); -- Check that To_Script_String (C) returns Expected ----------------- -- Create_Args -- ----------------- function Create_Args (Command : String; Args : String_Array; Cmd_Mode : Command_Line_Mode; Arg_Mode : Argument_Mode) return Arg_List is Result : Arg_List := Parse_String (Command, Cmd_Mode); begin for A of Args loop Append_Argument (Result, +A, Arg_Mode); end loop; return Result; end Create_Args; ----------------------- -- Test_Parse_String -- ----------------------- procedure Test_Parse_String (Debug_String, Command, Text : String; Location : String := SI.Source_Location) is Args : constant Arg_List := Parse_String (Command, Text); begin A.Assert (To_Debug_String (Args), Debug_String, Location => Location); end Test_Parse_String; ----------------------- -- Test_Parse_String -- ----------------------- procedure Test_Parse_String (Debug_String, Text : String; Mode : Command_Line_Mode; Location : String := SI.Source_Location) is Args : constant Arg_List := Parse_String (Text, Mode); begin A.Assert (To_Debug_String (Args), Debug_String, Location => Location); end Test_Parse_String; ---------------------------------- -- Test_Argument_List_To_String -- ---------------------------------- procedure Test_Argument_List_To_String (Expected : String; List : String_Array; Protect_Quotes : Boolean) is use GNAT.Strings; SList : String_List_Access := new String_List (List'Range); begin for I in List'Range loop SList (I) := new String'(+List (I)); end loop; A.Assert (Expected, Argument_List_To_String (SList.all, Protect_Quotes)); GNAT.Strings.Free (SList); end Test_Argument_List_To_String; ------------------ -- Test_To_List -- ------------------ procedure Test_To_List (Expected : String_Array; C : String_Array; Include_Command : Boolean; Location : String := SI.Source_Location) is use GNAT.Strings; Args : Arg_List := Empty_Command_Line; begin for A of C loop Append_Argument (Args, +A, One_Arg); end loop; declare List : constant String_List := To_List (Args, Include_Command); begin A.Assert (List'Length = Expected'Length, Msg => "checking length", Location => Location); for I in Expected'Range loop declare Expected_Arg : constant String := +Expected (I); Got_Arg : String_Access := List (List'First + I - Expected'First); begin A.Assert (Expected_Arg, Got_Arg.all, Msg => "Checking argument no. " & I'Image, Location => Location); Free (Got_Arg); end; end loop; end; end Test_To_List; --------------------------- -- Substitution_Callback -- --------------------------- function Substitution_Callback (Param : String; Mode : Command_Line_Mode) return Arg_List is pragma Unreferenced (Mode); Result : Arg_List; begin Append_Argument (Result, "<<", One_Arg); Append_Argument (Result, Param, One_Arg); Append_Argument (Result, ">>", One_Arg); return Result; end Substitution_Callback; --------------------- -- Test_Substitute -- --------------------- procedure Test_Substitute (Debug_String : String; Command : String := "cmd"; Args : String_Array; Cmd_Mode : Command_Line_Mode; Arg_Mode : Argument_Mode; Location : String := SI.Source_Location) is Cmd_Line : Arg_List := Create_Args (Command, Args, Cmd_Mode, Arg_Mode); begin Substitute (Cmd_Line, '%', Substitution_Callback'Access); A.Assert (To_Debug_String (Cmd_Line), Debug_String, Location => Location); end Test_Substitute; ---------------------------- -- Test_To_Display_String -- ---------------------------- procedure Test_To_Display_String (Expected : String; C : Arg_List; Include_Command : Boolean; Max_Arg_Length : Positive := Positive'Last) is begin A.Assert (Expected, To_Display_String (C, Include_Command, Max_Arg_Length)); end Test_To_Display_String; --------------------------- -- Test_To_Script_String -- --------------------------- procedure Test_To_Script_String (Expected : String; C : Arg_List) is begin A.Assert (Expected, To_Script_String (C)); end Test_To_Script_String; begin ------------------------ -- Get_Command/Create -- ------------------------ A.Assert (Get_Command (Create ("foo")), "foo"); A.Assert (Get_Command (Empty_Command_Line), ""); ----------------------------- -- Argument_List_To_String -- ----------------------------- Test_Argument_List_To_String (Expected => "foo bar\ baz", List => (+"foo", +"bar baz"), Protect_Quotes => True); Test_Argument_List_To_String (Expected => "foo bar baz", List => (+"foo", +"bar baz"), Protect_Quotes => False); ------------------ -- Parse_String -- ------------------ Test_Parse_String (Debug_String => "Command: foo" & ASCII.LF & "Arg: bar" & ASCII.LF & "Arg: baz", Command => "foo", Text => "bar baz"); Test_Parse_String (Debug_String => "Command: foo" & ASCII.LF & "Arg: bar" & ASCII.LF & "Arg: baz", Text => "foo bar baz", Mode => Separate_Args); Test_Parse_String (Debug_String => "Command: foo bar baz", Text => "foo bar baz", Mode => Raw_String); Test_Parse_String (Debug_String => "Command: ", Text => "", Mode => Raw_String); Test_Parse_String (Debug_String => "Command: foo" & ASCII.LF & "Arg: a" & ASCII.LF & "Arg: " & ASCII.LF & "Arg: b c" & ASCII.LF & "Arg: d", Command => "foo", Text => "a """" ""b c"" ""d"""); ----------------------------------------------------- -- Append_Argument/Args_Length/Nth_ARg/Set_Nth_Arg -- ----------------------------------------------------- declare Args : Arg_List := Create ("cmd"); begin A.Assert (Args_Length (Args) = 0); A.Assert (Nth_Arg (Args, 0), "cmd"); Append_Argument (Args, "a b", Expandable); A.Assert (Args_Length (Args) = 1); A.Assert (Nth_Arg (Args, 1), "a b"); -- Out-of-ounds argument access must raise a Constraint_Error declare Exception_Raised : Boolean := False; begin begin declare Dummy : constant String := Nth_Arg (Args, 2); begin null; end; exception when Constraint_Error => Exception_Raised := True; end; A.Assert (Exception_Raised); end; Set_Nth_Arg (Args, 3, "c"); A.Assert (Args_Length (Args) = 3); A.Assert (Nth_Arg (Args, 1), "a b"); A.Assert (Nth_Arg (Args, 2), ""); A.Assert (Nth_Arg (Args, 3), "c"); A.Assert (+Nth_Arg (Args, 3), "c"); end; ------------------ -- Substitution -- ------------------ Test_Substitute (Debug_String => "Command: cmd" & ASCII.LF & "Arg: %a" & ASCII.LF & "Arg: ", Args => (+"%a", +""), Cmd_Mode => Raw_String, Arg_Mode => One_Arg); Test_Substitute (Debug_String => "Command: << cmd >>" & ASCII.LF & "Arg: %a", Command => "%cmd", Args => (1 => +"%a"), Cmd_Mode => Raw_String, Arg_Mode => One_Arg); Test_Substitute (Debug_String => "Command: cmd" & ASCII.LF & "Arg: << a >>" & ASCII.LF & "Arg: %" & ASCII.LF & "Arg: ", Args => (+"%a", +"%%", +""), Cmd_Mode => Separate_Args, Arg_Mode => One_Arg); Test_Substitute (Debug_String => "Command: cmd" & ASCII.LF & "Arg: <<" & ASCII.LF & "Arg: a" & ASCII.LF & "Arg: >>" & ASCII.LF & "Arg: <<" & ASCII.LF & "Arg: %" & ASCII.LF & "Arg: >>", Args => (+"%a", +"%%"), Cmd_Mode => Separate_Args, Arg_Mode => Expandable); Test_Substitute (Debug_String => "Command: cmd" & ASCII.LF & "Arg: zz<< a >> bzz" & ASCII.LF & "Arg: zz<< a b >>zz", Args => (+"zz%a bzz", +"zz%{a b}zz"), Cmd_Mode => Separate_Args, Arg_Mode => Expandable); Test_Substitute (Debug_String => "Command: << \\a >>", Command => "%{\a}", Args => (1 .. 0 => <>), Cmd_Mode => Raw_String, Arg_Mode => Expandable); Test_Substitute (Debug_String => "Command: cmd" & ASCII.LF & "Arg: --option=%", Args => (1 => +"--option=%"), Cmd_Mode => Separate_Args, Arg_Mode => Expandable); -- Make sure that Substitute works on an empty command line declare Args : Arg_List := Empty_Command_Line; begin Substitute (Args, '%', Substitution_Callback'Access); A.Assert (Args = Empty_Command_Line); end; -- Make sure that substitute does nothing when passed a null callback declare Args : constant Arg_List := Parse_String ("cmd", "%a"); New_Args : Arg_List := Args; begin Substitute (New_Args, '%', null); A.Assert (Args = New_Args); end; Test_To_List (Expected => (+"b", +"c"), C => (+"a", +"b", +"c"), Include_Command => False); Test_To_List (Expected => (+"a", +"b", +"c"), C => (+"a", +"b", +"c"), Include_Command => True); --------------------------- -- Conversions to string -- --------------------------- -- To_Display_String Test_To_Display_String (Expected => "cmd", C => Parse_String ("cmd", ""), Include_Command => True); Test_To_Display_String (Expected => "", C => Parse_String ("cmd", ""), Include_Command => False); Test_To_Display_String (Expected => "cmd arg1 arg2", C => Parse_String ("cmd", "arg1 arg2"), Include_Command => True); Test_To_Display_String (Expected => "cmd 0123456789", C => Parse_String ("cmd", "0123456789"), Include_Command => True, Max_Arg_Length => 10); Test_To_Display_String (Expected => "cmd 012345678...", C => Parse_String ("cmd", "0123456789abcdef"), Include_Command => True, Max_Arg_Length => 10); -- To_String_String Test_To_Script_String (Expected => "", C => Empty_Command_Line); Test_To_Script_String (Expected => "cmd", C => Parse_String ("cmd", Raw_String)); declare C : Arg_List := Parse_String ("cmd", Raw_String); begin Append_Argument (C, "arg", One_Arg); Test_To_Script_String (Expected => "cmd", C => C); end; Test_To_Script_String (Expected => "cmd arg", C => Parse_String ("cmd", "arg")); Test_To_Script_String (Expected => "cmd arg\\1 arg\ 2 arg\""3", C => Create_Args (Command => "cmd", Args => (+"arg\1", +"arg 2", +"arg""3"), Cmd_Mode => Separate_Args, Arg_Mode => Expandable)); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/arg_lists/test.yaml0000644000175000017500000000007313661715457023231 0ustar nicolasnicolasdescription: Extensive test for the GNATCOLL.Arg_Lists API gnatcoll-core-21.0.0/testsuite/tests/traces/0000755000175000017500000000000013661715457020660 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/traces/parse_config_file/0000755000175000017500000000000013661715457024316 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/traces/parse_config_file/test.adb0000644000175000017500000000042713661715457025750 0ustar nicolasnicolaswith GNATCOLL.Traces; with Test_Assert; function Test return Integer is package Traces renames GNATCOLL.Traces; package A renames Test_Assert; begin Traces.Parse_Config_File ("my.cfg"); A.Assert (True, "should not raise an exception"); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/traces/parse_config_file/test.yaml0000644000175000017500000000014613661715457026162 0ustar nicolasnicolastitle: GNATCOLL.Traces.Parse_Config_File description: Test parsing of config files data: - my.cfg gnatcoll-core-21.0.0/testsuite/tests/traces/parse_config_file/my.cfg0000644000175000017500000000026513661715457025427 0ustar nicolasnicolas+ >>log PLCTIME=yes >> logPLCTIME PLCCOMM=yes >> logPLCCOMM DEBUG.COLORS=no DEBUG.ABSOLUTE_TIME=yes DEBUG.STACK_TRACE=no DEBUG.COUNT=no DEBUG.ENCLOSING_ENTITY=no DEBUG.LOCATION=yes gnatcoll-core-21.0.0/testsuite/tests/traces/trace_exists/0000755000175000017500000000000013661715457023355 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/traces/trace_exists/test.adb0000644000175000017500000000123313661715457025003 0ustar nicolasnicolaswith Test_Assert; with GNATCOLL.Traces; use GNATCOLL.Traces; function Test return Integer is Name : constant String := "FooBar"; Trace : Trace_Handle; begin Test_Assert.Assert (not Exists (Name), "The trace should not be defined"); Trace := Create (Name, Off); Test_Assert.Assert (not Active (Trace), "The trace should be disabled"); Test_Assert.Assert (Exists (Name), "The trace should be defined"); Trace := Create (Name, On); Test_Assert.Assert (not Active (Trace), "The trace should still be disabled"); Test_Assert.Assert (Exists (Name), "The trace should be defined"); return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/traces/trace_exists/test.yaml0000644000175000017500000000013313661715457025215 0ustar nicolasnicolastitle: GNATCOLL.Traces.Exists description: Test to detect if a trace exists in the code gnatcoll-core-21.0.0/testsuite/tests/locks/0000755000175000017500000000000013661715457020512 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/locks/scoped/0000755000175000017500000000000013661715457021767 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/locks/scoped/test.adb0000644000175000017500000000461313661715457023422 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Test_Assert; with GNATCOLL.Locks; use GNATCOLL.Locks; function Test return Integer is package A renames Test_Assert; M : aliased Mutual_Exclusion; Counter : Natural := 0; procedure Increment; procedure Increment is Dummy : Scoped_Lock (M'Access); begin Counter := Counter + 1; end Increment; task type Worker is entry Stop; end Worker; task body Worker is begin for I in 1 .. 1_000 loop Increment; end loop; accept Stop; end Worker; Pool : array (1 .. 10) of Worker; begin for T of Pool loop T.Stop; end loop; A.Assert (Counter = 10_000, "Atomic increment"); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/locks/scoped/test.yaml0000644000175000017500000000005513661715457023632 0ustar nicolasnicolasdescription: Simple test for scoped lock gnatcoll-core-21.0.0/testsuite/tests/vfs/0000755000175000017500000000000013661715457020175 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/vfs/create_from_dir/0000755000175000017500000000000013661715457023321 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/vfs/create_from_dir/test.adb0000644000175000017500000000766413661715457024765 0ustar nicolasnicolaswith GNAT.OS_Lib; use GNAT.OS_Lib; with GNATCOLL.VFS; use GNATCOLL.VFS; with Test_Assert; function Test return Integer is package A renames Test_Assert; DS : constant Character := GNAT.OS_Lib.Directory_Separator; Folder : Virtual_File; F : Virtual_File; begin ------------ -- Case 1 -- ------------ -- Using a directory that is not a full name (and where the .. cannot -- be resolved) Folder := Create (+".." & DS & ".." & DS & "foo" & DS); F := Create_From_Dir (Dir => Folder, Base_Name => +"file.ads"); A.Assert (".." & DS & ".." & DS & "foo" & DS & "file.ads", F.Display_Full_Name, "With base name and relative directory"); A.Assert (".." & DS & ".." & DS & "foo" & DS & "file.ads", F.Display_Full_Name (Normalize => True), "Normalized with base name and relative directory"); F := Create_From_Dir (Dir => Folder, Base_Name => +"file.ads", Normalize => True); A.Assert (".." & DS & ".." & DS & "foo" & DS & "file.ads", F.Display_Full_Name, "With base name and relative directory"); A.Assert (".." & DS & ".." & DS & "foo" & DS & "file.ads", F.Display_Full_Name (Normalize => True), "Normalized with base name and relative directory"); ------------ -- Case 2 -- ------------ Folder := Create (+"foo1" & DS & "foo2" & DS & "foo3" & DS & ""); F := Create_From_Dir (Dir => Folder, Base_Name => +".." & DS & ".." & DS & "file.ads"); A.Assert ("foo1" & DS & "foo2" & DS & "foo3" & DS & ".." & DS & ".." & DS & "file.ads", F.Display_Full_Name, "When file is relative to absolute directory"); A.Assert ("foo1" & DS & "file.ads", F.Display_Full_Name (Normalize => True), "Normalized name when file is relative to absolute directory"); F := Create_From_Dir ( Dir => Folder, Base_Name => +".." & DS & ".." & DS & "file.ads", Normalize => True); A.Assert ("foo1" & DS & "foo2" & DS & "foo3" & DS & ".." & DS & ".." & DS & "file.ads", F.Display_Full_Name, "When file is relative to normalized absolute directory"); A.Assert ("foo1" & DS & "file.ads", F.Display_Full_Name (Normalize => True), "Normalized name when file is relative to normalized absolute" & " directory"); ------------ -- Case 3 -- ------------ -- Using an absolute directory, where the .. can be resolved Folder := Create (+"" & DS & "foo1" & DS & "foo2" & DS & "foo3" & DS & ".." & DS & ".." & DS & "foo" & DS & ""); F := Create_From_Dir (Dir => Folder, Base_Name => +"file.ads"); A.Assert ("" & DS & "foo1" & DS & "foo2" & DS & "foo3" & DS & ".." & DS & ".." & DS & "foo" & DS & "file.ads", F.Display_Full_Name, "When file is relative to absolute directory with .."); A.Assert ("" & DS & "foo1" & DS & "foo" & DS & "file.ads", F.Display_Full_Name (Normalize => True), "Normalized name when file is relative to absolute directory" & " with .."); F := Create_From_Dir (Dir => Folder, Base_Name => +"file.ads", Normalize => True); A.Assert ("" & DS & "foo1" & DS & "foo" & DS & "file.ads", F.Display_Full_Name, "When file is relative to normalized absolute directory with .."); A.Assert ("" & DS & "foo1" & DS & "foo" & DS & "file.ads", F.Display_Full_Name (Normalize => True), "Normalized name when file is relative to normalized absolute" & " directory with .."); -- Check Create_From_Dir with a No_File entry declare Test_Success : Boolean := False; begin begin F := Create_From_Dir (No_File, +"file.ads"); exception when VFS_Invalid_File_Error => Test_Success := True; end; A.Assert (Test_Success, "Should raise Invalid_File_Error"); end; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/vfs/create_from_dir/test.yaml0000644000175000017500000000004213661715457025160 0ustar nicolasnicolasdescription: Test Create_From_Dir gnatcoll-core-21.0.0/testsuite/tests/vfs/create/0000755000175000017500000000000013661715457021440 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/vfs/create/test.adb0000644000175000017500000000321113661715457023064 0ustar nicolasnicolaswith GNATCOLL.VFS; use GNATCOLL.VFS; with Ada.Directories; with Test_Assert; function Test return Integer is package Dir renames Ada.Directories; package A renames Test_Assert; begin Dir.Create_Directory ("test_subdir"); Dir.Set_Directory ("./test_subdir"); declare -- All the files refer to the same file using different paths -- (we assume the current directory is test_subdir). File1 : constant String := "main.adb"; File2 : constant String := "./main.adb"; File3 : constant String := "../test_subdir/main.adb"; NormFile1 : constant Virtual_File := Create (+File1, Normalize => True); NormFile2 : constant Virtual_File := Create (+File2, Normalize => True); NormFile3 : constant Virtual_File := Create (+File3, Normalize => True); -- Create an absolute path. Create_From_Dir calls Create -- with an aboslute path, so this covers the special case -- in Create. NormFile4 : constant Virtual_File := Create_From_Dir (Create ("./", Normalize => True), "main.adb"); begin -- Ensure that whatever version of the path was used to reference -- main.adb, then call to functions on that path (such as Get_Parent), -- will return the same value. A.Assert (+NormFile1.Get_Parent.Get_Parent.Full_Name = +NormFile2.Get_Parent.Get_Parent.Full_Name); A.Assert (+NormFile2.Get_Parent.Get_Parent.Full_Name = +NormFile3.Get_Parent.Get_Parent.Full_Name); A.Assert (+NormFile3.Get_Parent.Get_Parent.Full_Name = +NormFile4.Get_Parent.Get_Parent.Full_Name); end; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/vfs/create/test.yaml0000644000175000017500000000005213661715457023300 0ustar nicolasnicolasdescription: Test for GNATCOLL.VFS.Create gnatcoll-core-21.0.0/testsuite/tests/vfs/basic/0000755000175000017500000000000013661715457021256 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/vfs/basic/test.adb0000644000175000017500000001645113661715457022714 0ustar nicolasnicolas------------------------------------------------------------------------------ -- -- -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.VFS; use GNATCOLL.VFS; with GNAT.Strings; use GNAT.Strings; with GNATCOLL.OS.Constants; use GNATCOLL.OS, GNATCOLL.OS.Constants; with Ada.Directories; with Ada.Containers.Hashed_Maps; with Interfaces.C.Strings; with Test_Assert; function Test return Integer is package A renames Test_Assert; package AD renames Ada.Directories; procedure Test_F (Dir : Virtual_File); -- Perform tests on a specific file procedure Test_F (Dir : Virtual_File) is D, F : Virtual_File; W : Writable_File; Str : String_Access; Success : Boolean; Dirs, Files : File_Array_Access; begin -- A file that does not exist yet F := Create_From_Dir (Dir => Dir, Base_Name => "foo.txt"); A.Assert (+Base_Name (F), "foo.txt", "base name"); A.Assert (+Full_Name (F), +Full_Name (Dir) & "foo.txt", "full name"); A.Assert (+Dir_Name (F), +Full_Name (Dir), "dir name"); A.Assert (+F.File_Extension, ".txt", "file extension"); A.Assert (Is_Absolute_Path (F), "is absolute"); A.Assert (not Is_Regular_File (F), "is regular file"); A.Assert (+Relative_Path (F, Dir), "foo.txt", "relative path"); A.Assert (Has_Suffix (F, "t"), "has suffix"); A.Assert (not Is_Symbolic_Link (F), "is symlink"); -- Now create the file W := Write_File (F); Write (W, "first word "); Close (W); W := Write_File (F, Append => True); Write (W, Interfaces.C.Strings.New_String ("second word")); Close (W); -- Check whether the file exists A.Assert (Is_Regular_File (F), "is regular file after creation"); A.Assert (Is_Writable (F), "is writable after creation"); -- Make the file unreadable Set_Readable (F, False); A.Assert (Is_Regular_File (F), "is regular file when unreadable"); A.Assert (not Is_Readable (F) or else OS = Windows, "is readable"); -- Try and read the file Str := Read_File (F); if Str /= null then A.Assert (False, "can read unreadable file?"); end if; Free (Str); -- Make it readable again, and read again Set_Readable (F, True); Str := Read_File (F); A.Assert (Str.all, "first word second word", "contents when readable"); A.Assert (Integer (Size (F)), Str.all'Length, "file size"); Free (Str); -- Make the file read-only Set_Writable (F, False); A.Assert (not Is_Writable (F) or else OS = Windows, "is writable"); -- Check directory operations A.Assert (Is_Directory (Dir), "is directory"); D := Create_From_Dir (Dir, "sub/sub1"); Make_Dir (D, Recursive => True); W := Write_File (Create_From_Dir (D, "foo")); Close (W); W := Write_File (Create_From_UTF8 (+Full_Name (D) & "/bar")); Close (W); Rename (D / "bar", Create_From_Dir (D, "bar.txt"), Success); A.Assert (Success, "rename"); Copy (D, "sub/sub2", Success); A.Assert (Success, "copy"); Remove_Dir (D, Recursive => False, Success => Success); A.Assert (not Success, "remove dir (non-recursive)"); Dirs := Read_Dir_Recursive (Create_From_Dir (Dir, "sub"), Filter => Dirs_Only); A.Assert (Dirs.all'Length, 2, "dirs found"); Files := Read_Files_From_Dirs (Dirs.all); A.Assert (Files.all'Length, 4, "files found"); Unchecked_Free (Files); Unchecked_Free (Dirs); Remove_Dir (D, Recursive => True, Success => Success); A.Assert (Success, "remove dir (recursive)"); Files := Read_Dir_Recursive (Dir, Extension => ".txt"); A.Assert (Files.all'Length, 2, "txt files found"); Unchecked_Free (Files); -- Delete the file Delete (F, Success); A.Assert (Success or else OS = Windows, "could delete"); Delete (F, Success); A.Assert (not Success, "could delete again"); end Test_F; Cur_Dir : constant Virtual_File := Get_Current_Dir; Cur_Dir_AD : constant String := AD.Current_Directory & Dir_Sep; begin A.Assert (+Dir_Name (Cur_Dir), Cur_Dir_AD, "current directory"); A.Assert (+Base_Name (Cur_Dir), "", "base name of dir"); Test_F (Cur_Dir); -- Try manipulating No_File A.Assert (+Base_Name (No_File), "", "base name"); A.Assert (+Full_Name (No_File), "", "full name"); A.Assert (+Dir_Name (No_File), "", "dir name"); A.Assert (+File_Extension (No_File), "", "file extension"); A.Assert (not Is_Absolute_Path (No_File), "is absolute"); A.Assert (not Is_Regular_File (No_File), "is regular file"); -- Comparisons declare Default_Pref_Py : constant Virtual_File := Create_From_Dir (Dir => Get_Current_Dir, Base_Name => "default_pref.py"); Default_Pref_Pyc : constant Virtual_File := Create_From_Dir (Dir => Get_Current_Dir, Base_Name => "default_pref.pyc"); package Maps is new Ada.Containers.Hashed_Maps (Virtual_File, Integer, Full_Name_Hash, "="); use Maps; M : Maps.Map; C : Maps.Cursor; F1, F2 : Virtual_File; begin A.Assert (Default_Pref_Py < Default_Pref_Pyc, "default_pref.py < default_pref.pyc"); A.Assert (not (Default_Pref_Pyc < Default_Pref_Py), "default_pref.pyc < default_pref.py"); -- Check mapping functionality F1 := Create ("/a/b"); M.Include (F1, 2); F2 := Create ("/a/b/c"); F2 := F2.Get_Parent; -- "/a/b/" C := M.Find (F2); A.Assert (Has_Element (C), "find parent in map"); end; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/vfs/basic/test.yaml0000644000175000017500000000005213661715457023116 0ustar nicolasnicolasdescription: Basic test for GNATCOLL.VFS. gnatcoll-core-21.0.0/testsuite/tests/json/0000755000175000017500000000000013661715457020350 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/json/integer_number/0000755000175000017500000000000013661715457023355 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/json/integer_number/test.adb0000644000175000017500000000131613661715457025005 0ustar nicolasnicolaswith GNATCOLL.JSON; with Test_Assert; function Test return Integer is package A renames Test_Assert; package JSON renames GNATCOLL.JSON; use type JSON.JSON_Value_Type; Content : constant String := "14"; begin declare Value : constant JSON.JSON_Value := JSON.Read (Strm => Content, Filename => ""); Result : Integer; begin A.Assert (True, "passed"); A.Assert (JSON.Kind (Value) = JSON.JSON_Int_Type, "check if type is JSON_Int_Type (got " & JSON.Kind (Value)'Img & ")"); Result := JSON.Get (Value); A.Assert (Result = 14, "check that result is equal to 14"); end; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/json/integer_number/test.yaml0000644000175000017500000000010513661715457025214 0ustar nicolasnicolasDescription: Ensure that obvious integers are not converted to float gnatcoll-core-21.0.0/testsuite/tests/json/invalid/0000755000175000017500000000000013661715457021776 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_object.json0000644000175000017500000000000113661715457027604 0ustar nicolasnicolas{gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_just_comma.json0000644000175000017500000000000313661715457026536 0ustar nicolasnicolas[,]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_single_doublequote.json0000644000175000017500000000000113661715457030454 0ustar nicolasnicolas"gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_backslash_00.json0000644000175000017500000000000613661715457027022 0ustar nicolasnicolas["\"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_expression.json0000644000175000017500000000000513661715457026750 0ustar nicolasnicolas[1+2]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_comma_instead_of_colon.json0000644000175000017500000000001313661715457031207 0ustar nicolasnicolas{"x", null}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_+Inf.json0000644000175000017500000000000613661715457025341 0ustar nicolasnicolas[+Inf]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_incomplete_null_token.json0000644000175000017500000000000213661715457027567 0ustar nicolasnicolasnugnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_U+FF11_fullwidth_digit_one.json0000644000175000017500000000000513661715457031511 0ustar nicolasnicolas[1]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_angle_bracket_..json0000644000175000017500000000000313661715457030315 0ustar nicolasnicolas<.>gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_end_array.json0000644000175000017500000000000113661715457027261 0ustar nicolasnicolas]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_incomplete_invalid_value.json0000644000175000017500000000000213661715457031435 0ustar nicolasnicolas[xgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_0.3e.json0000644000175000017500000000000613661715457025217 0ustar nicolasnicolas[0.3e]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_unterminated-value.json0000644000175000017500000000000713661715457030342 0ustar nicolasnicolas{"a":"agnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_escaped_emoji.json0000644000175000017500000000001113661715457027353 0ustar nicolasnicolas["\🌀"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_100000_opening_arrays.json0000644000175000017500000030324013661715457031150 0ustar nicolasnicolas[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_multiple_tokens.json0000644000175000017500000000001113661715457026414 0ustar nicolasnicolastruetrue gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_extra_comma.json0000644000175000017500000000000513661715457026676 0ustar nicolasnicolas["",]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_no_quotes_with_bad_escape.json0000644000175000017500000000000413661715457031763 0ustar nicolasnicolas[\n]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_emoji.json0000644000175000017500000000001213661715457025630 0ustar nicolasnicolas{🇨🇭}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_comma_and_number.json0000644000175000017500000000000413661715457027664 0ustar nicolasnicolas[,1]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_star_inside.json0000644000175000017500000000000313661715457026701 0ustar nicolasnicolas[*]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_++.json0000644000175000017500000000001013661715457024752 0ustar nicolasnicolas[++1234]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_unicode-identifier.json0000644000175000017500000000000213661715457031064 0ustar nicolasnicolasÃ¥gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_number_with_trailing_garbage.json0000644000175000017500000000000213661715457033202 0ustar nicolasnicolas2@gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_trailing_comment.json0000644000175000017500000000001513661715457030063 0ustar nicolasnicolas{"a":"b"}/**/gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_lone-open-bracket.json0000644000175000017500000000000113661715457030622 0ustar nicolasnicolas[gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_incomplete_surrogate_escape_invalid.json0000644000175000017500000000002213661715457034046 0ustar nicolasnicolas["\uD800\uD800\x"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_with_alpha.json0000644000175000017500000000001013661715457026665 0ustar nicolasnicolas[1.2a-3]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_unescaped_tab.json0000644000175000017500000000000513661715457027364 0ustar nicolasnicolas[" "]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_invalid-utf-8-in-bigger-int.json0000644000175000017500000000000613661715457031572 0ustar nicolasnicolas[123å]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_items_separated_by_semicolon.json0000644000175000017500000000000513661715457032312 0ustar nicolasnicolas[1:2]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_double_colon.json0000644000175000017500000000001213661715457027171 0ustar nicolasnicolas{"x"::"b"}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_invalid_utf8_after_escape.json0000644000175000017500000000000613661715457031665 0ustar nicolasnicolas["\å"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_1.0e.json0000644000175000017500000000000613661715457025215 0ustar nicolasnicolas[1.0e]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_number_and_several_commas.json0000644000175000017500000000000513661715457031571 0ustar nicolasnicolas[1,,]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_missing_key.json0000644000175000017500000000000613661715457027051 0ustar nicolasnicolas{:"b"}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_accentuated_char_no_quotes.json0000644000175000017500000000000413661715457032137 0ustar nicolasnicolas[é]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_newlines_unclosed.json0000644000175000017500000000001313661715457030116 0ustar nicolasnicolas["a", 4 ,1,gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_incomplete_true.json0000644000175000017500000000000513661715457026377 0ustar nicolasnicolas[tru]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_missing_value.json0000644000175000017500000000001113661715457027241 0ustar nicolasnicolas[ , ""]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_2E.json0000644000175000017500000000000213661715457025014 0ustar nicolasnicolas2Egnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_start_escape_unclosed.json0000644000175000017500000000000313661715457031136 0ustar nicolasnicolas["\gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_pi_in_key_and_trailing_comma.json0000644000175000017500000000001213661715457032362 0ustar nicolasnicolas{"¹":"0",}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_array_string.json0000644000175000017500000000000413661715457031045 0ustar nicolasnicolas["a"gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_array_trailing_garbage.json0000644000175000017500000000000413661715457031777 0ustar nicolasnicolas[1]xgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_object_with_trailing_garbage.json0000644000175000017500000000001713661715457033166 0ustar nicolasnicolas{"a": true} "x"gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_with_leading_zero.json0000644000175000017500000000000513661715457030246 0ustar nicolasnicolas[012]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_invalid_utf8.json0000644000175000017500000000000313661715457026771 0ustar nicolasnicolas[ÿ]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_incomplete_escape.json0000644000175000017500000000000513661715457030246 0ustar nicolasnicolas["\"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_colon_instead_of_comma.json0000644000175000017500000000000713661715457031062 0ustar nicolasnicolas["": 1]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_unclosed_array_partial_null.json0000644000175000017500000000001413661715457033101 0ustar nicolasnicolas[ false, nulgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_neg_real_without_int_part.json0000644000175000017500000000000713661715457032012 0ustar nicolasnicolas[-.123]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_neg_with_garbage_at_end.json0000644000175000017500000000000513661715457031337 0ustar nicolasnicolas[-1x]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_neg_int_starting_with_zero.json0000644000175000017500000000000613661715457032202 0ustar nicolasnicolas[-012]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_single_string_no_double_quotes.json0000644000175000017500000000000313661715457033062 0ustar nicolasnicolasabcgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_2.e+3.json0000644000175000017500000000000713661715457025275 0ustar nicolasnicolas[2.e+3]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_double_comma.json0000644000175000017500000000000613661715457027026 0ustar nicolasnicolas[1,,2]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_with_trailing_garbage.json0000644000175000017500000000001213661715457031041 0ustar nicolasnicolas{"a":"b"}#gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_no-colon.json0000644000175000017500000000000413661715457026252 0ustar nicolasnicolas{"a"gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_2.e3.json0000644000175000017500000000000613661715457025221 0ustar nicolasnicolas[2.e3]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_object_open_string.json0000644000175000017500000000000313661715457032215 0ustar nicolasnicolas{"agnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_single_star.json0000644000175000017500000000000113661715457027627 0ustar nicolasnicolas*gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_leading_uescaped_thinspace.json0000644000175000017500000000001513661715457032102 0ustar nicolasnicolas[\u0020"asd"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_two_commas_in_a_row.json0000644000175000017500000000002213661715457030553 0ustar nicolasnicolas{"a":"b",,"c":"d"}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_escape_x.json0000644000175000017500000000001013661715457026352 0ustar nicolasnicolas["\x00"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_-2..json0000644000175000017500000000000513661715457025045 0ustar nicolasnicolas[-2.]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_Inf.json0000644000175000017500000000000513661715457025265 0ustar nicolasnicolas[Inf]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_bad_value.json0000644000175000017500000000001413661715457026451 0ustar nicolasnicolas["x", truth]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_key_with_single_quotes.json0000644000175000017500000000001613661715457031315 0ustar nicolasnicolas{key: 'value'}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_0e+.json0000644000175000017500000000000513661715457025130 0ustar nicolasnicolas[0e+]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_single_quote.json0000644000175000017500000000000713661715457027227 0ustar nicolasnicolas{'a':0}././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_non_string_key_but_huge_number_instead.jsongnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_non_string_key_but_huge_number_instead.js0000644000175000017500000000001513661715457034164 0ustar nicolasnicolas{9999E9999:1}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_1_surrogate_then_escape.json0000644000175000017500000000001313661715457031357 0ustar nicolasnicolas["\uD800\"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_whitespace_formfeed.json0000644000175000017500000000000313661715457031322 0ustar nicolasnicolas[ ]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_bracket_key.json0000644000175000017500000000001113661715457027007 0ustar nicolasnicolas{[: "x"} gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_unquoted_key.json0000644000175000017500000000001013661715457027237 0ustar nicolasnicolas{a: "b"}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_unclosed_with_new_lines.json0000644000175000017500000000001013661715457031305 0ustar nicolasnicolas[1, 1 ,1gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_spaces_vertical_tab_formfeed.json0000644000175000017500000000001013661715457032237 0ustar nicolasnicolas[" a"\f]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_minus_sign_with_trailing_garbage.json0000644000175000017500000000000613661715457033321 0ustar nicolasnicolas[-foo]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_escaped_backslash_bad.json0000644000175000017500000000000713661715457031016 0ustar nicolasnicolas["\\\"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_.-1.json0000644000175000017500000000000513661715457025044 0ustar nicolasnicolas[.-1]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_0.1.2.json0000644000175000017500000000000713661715457025211 0ustar nicolasnicolas[0.1.2]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/test.adb0000644000175000017500000000214413661715457023426 0ustar nicolasnicolaswith Test_Assert; with GNATCOLL.JSON; with GNATCOLL.VFS; with GNAT.Strings; with Ada.Command_Line; with Ada.Exceptions; function Test return Integer is package A renames Test_Assert; package JSON renames GNATCOLL.JSON; package VFS renames GNATCOLL.VFS; procedure Test (JSON_String : String); procedure Test (JSON_String : String) is begin declare pragma Warnings (Off); V : JSON.JSON_Value; pragma Warning (On); begin V := JSON.Read (JSON_String); A.Assert (False, "invalid json (exception not raised)"); exception when JSON.Invalid_JSON_Stream => A.Assert (True, "failed with Invalid_JSON_Stream"); when E : others => A.Assert (False, "invalid json: (wrong exception)" & ASCII.LF & Ada.Exceptions.Exception_Information (E)); end; end Test; File_Content : GNAT.Strings.String_Access := VFS.Read_File (VFS.Create (VFS."+" (Ada.Command_Line.Argument (1)))); begin Test (File_Content.all); GNAT.Strings.Free (File_Content); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_with_trailing_garbage.json0000644000175000017500000000000313661715457031101 0ustar nicolasnicolas""xgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_ascii-unicode-identifier.json0000644000175000017500000000000313661715457032153 0ustar nicolasnicolasaÃ¥gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_1_surrogate_then_escape_u1.json0000644000175000017500000000001513661715457031766 0ustar nicolasnicolas["\uD800\u1"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_real_with_invalid_utf8_after_e.json0000644000175000017500000000000513661715457032670 0ustar nicolasnicolas[1eå]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_unclosed_array_unfinished_false.json0000644000175000017500000000001413661715457033721 0ustar nicolasnicolas[ true, falsgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_0.3e+.json0000644000175000017500000000000713661715457025273 0ustar nicolasnicolas[0.3e+]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_unclosed.json0000644000175000017500000000000313661715457026211 0ustar nicolasnicolas[""gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_missing_semicolon.json0000644000175000017500000000001113661715457030245 0ustar nicolasnicolas{"a" "b"}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_non_string_key.json0000644000175000017500000000000513661715457027557 0ustar nicolasnicolas{1:1}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_2e-.json0000644000175000017500000000000313661715457025132 0ustar nicolasnicolas2e-gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_invalid-utf-8-in-int.json0000644000175000017500000000000513661715457030334 0ustar nicolasnicolas[0å] gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_several_trailing_commas.json0000644000175000017500000000001513661715457031421 0ustar nicolasnicolas{"id":0,,,,,}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_with_alpha_char.json0000644000175000017500000000003113661715457027665 0ustar nicolasnicolas[1.8011670033376514H-308]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_multidigit_number_then_00.json0000644000175000017500000000000413661715457030240 0ustar nicolasnicolas123gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_invalid_backslash_esc.json0000644000175000017500000000000613661715457031063 0ustar nicolasnicolas["\a"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_minus_space_1.json0000644000175000017500000000000513661715457027277 0ustar nicolasnicolas[- 1]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_comma_instead_of_closing_brace.json0000644000175000017500000000001313661715457033461 0ustar nicolasnicolas{"x": true,gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_array_object.json0000644000175000017500000075022113661715457031022 0ustar nicolasnicolas[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"": gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_.2e-3.json0000644000175000017500000000000713661715457025277 0ustar nicolasnicolas[.2e-3]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_escaped_ctrl_char_tab.json0000644000175000017500000000000613661715457031043 0ustar nicolasnicolas["\ "]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_hex_1_digit.json0000644000175000017500000000000513661715457026735 0ustar nicolasnicolas[0x1]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_1_true_without_comma.json0000644000175000017500000000001013661715457030531 0ustar nicolasnicolas[1 true]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_0e.json0000644000175000017500000000000413661715457025054 0ustar nicolasnicolas[0e]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_object_followed_by_closing_object.json0000644000175000017500000000000313661715457034216 0ustar nicolasnicolas{}}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_incomplete_false.json0000644000175000017500000000000613661715457026513 0ustar nicolasnicolas[fals]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_array_open_string.json0000644000175000017500000000000313661715457032065 0ustar nicolasnicolas["agnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_unescaped_crtl_char.json0000644000175000017500000000000713661715457030561 0ustar nicolasnicolas["aa"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_0_capital_E.json0000644000175000017500000000000413661715457026650 0ustar nicolasnicolas[0E]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_real_garbage_after_e.json0000644000175000017500000000000513661715457030631 0ustar nicolasnicolas[1ea]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_U+2060_word_joined.json0000644000175000017500000000000513661715457030473 0ustar nicolasnicolas[â ]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_2.e-3.json0000644000175000017500000000000713661715457025277 0ustar nicolasnicolas[2.e-3]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_-NaN.json0000644000175000017500000000000613661715457025303 0ustar nicolasnicolas[-NaN]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_trailing_#.json0000644000175000017500000000001413661715457027334 0ustar nicolasnicolas{"a":"b"}#{}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_hex_2_digits.json0000644000175000017500000000000613661715457027122 0ustar nicolasnicolas[0x42]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_double_array.json0000644000175000017500000000000413661715457027770 0ustar nicolasnicolas[][]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/test.yaml0000644000175000017500000003145513661715457023651 0ustar nicolasnicolasdescription: Check behavior of JSON parser on invalid JSON driver: data_validation data_files: "n_array_1_true_without_comma.json": "n array 1 true without comma" "n_array_a_invalid_utf8.json": "n array a invalid utf8" "n_array_colon_instead_of_comma.json": "n array colon instead of comma" "n_array_comma_after_close.json": "n array comma after close" "n_array_comma_and_number.json": "n array comma and number" "n_array_double_comma.json": "n array double comma" "n_array_double_extra_comma.json": "n array double extra comma" "n_array_extra_close.json": "n array extra close" "n_array_extra_comma.json": "n array extra comma" "n_array_incomplete.json": "n array incomplete" "n_array_incomplete_invalid_value.json": "n array incomplete invalid value" "n_array_inner_array_no_comma.json": "n array inner array no comma" "n_array_invalid_utf8.json": "n array invalid utf8" "n_array_items_separated_by_semicolon.json": "n array items separated by semicolon" "n_array_just_comma.json": "n array just comma" "n_array_just_minus.json": "n array just minus" "n_array_missing_value.json": "n array missing value" "n_array_newlines_unclosed.json": "n array newlines unclosed" "n_array_number_and_comma.json": "n array number and comma" "n_array_number_and_several_commas.json": "n array number and several commas" "n_array_spaces_vertical_tab_formfeed.json": "n array spaces vertical tab formfeed" "n_array_star_inside.json": "n array star inside" "n_array_unclosed.json": "n array unclosed" "n_array_unclosed_trailing_comma.json": "n array unclosed trailing comma" "n_array_unclosed_with_new_lines.json": "n array unclosed with new lines" "n_array_unclosed_with_object_inside.json": "n array unclosed with object inside" "n_incomplete_false.json": "n incomplete false" "n_incomplete_null.json": "n incomplete null" "n_incomplete_true.json": "n incomplete true" "n_multidigit_number_then_00.json": "n multidigit number then 00" "n_number_++.json": "n number ++" "n_number_-.json": "n number -" "n_number_2E.json": "n number 2E" "n_number_2e-.json": "n number 2e-" "n_number_+1.json": "n number +1" "n_number_+Inf.json": "n number +Inf" "n_number_-01.json": "n number -01" "n_number_-1.0..json": "n number -1.0." "n_number_-2..json": "n number -2." "n_number_-NaN.json": "n number -NaN" "n_number_.-1.json": "n number .-1" "n_number_.2e-3.json": "n number .2e-3" "n_number_0.1.2.json": "n number 0.1.2" "n_number_0.3e+.json": "n number 0.3e+" "n_number_0.3e.json": "n number 0.3e" "n_number_0.e1.json": "n number 0.e1" "n_number_0_capital_E+.json": "n number 0 capital E+" "n_number_0_capital_E.json": "n number 0 capital E" "n_number_0e+.json": "n number 0e+" "n_number_0e.json": "n number 0e" "n_number_1.0e+.json": "n number 1.0e+" "n_number_1.0e-.json": "n number 1.0e-" "n_number_1.0e.json": "n number 1.0e" "n_number_1_000.json": "n number 1 000" "n_number_1eE2.json": "n number 1eE2" "n_number_2.e+3.json": "n number 2.e+3" "n_number_2.e-3.json": "n number 2.e-3" "n_number_2.e3.json": "n number 2.e3" "n_number_9.e+.json": "n number 9.e+" "n_number_Inf.json": "n number Inf" "n_number_NaN.json": "n number NaN" "n_number_U+FF11_fullwidth_digit_one.json": "n number U+FF11 fullwidth digit one" "n_number_expression.json": "n number expression" "n_number_hex_1_digit.json": "n number hex 1 digit" "n_number_hex_2_digits.json": "n number hex 2 digits" "n_number_infinity.json": "n number infinity" "n_number_invalid+-.json": "n number invalid+-" "n_number_invalid-negative-real.json": "n number invalid-negative-real" "n_number_invalid-utf-8-in-bigger-int.json": "n number invalid-utf-8-in-bigger-int" "n_number_invalid-utf-8-in-exponent.json": "n number invalid-utf-8-in-exponent" "n_number_invalid-utf-8-in-int.json": "n number invalid-utf-8-in-int" "n_number_minus_infinity.json": "n number minus infinity" "n_number_minus_sign_with_trailing_garbage.json": "n number minus sign with trailing garbage" "n_number_minus_space_1.json": "n number minus space 1" "n_number_neg_int_starting_with_zero.json": "n number neg int starting with zero" "n_number_neg_real_without_int_part.json": "n number neg real without int part" "n_number_neg_with_garbage_at_end.json": "n number neg with garbage at end" "n_number_real_garbage_after_e.json": "n number real garbage after e" "n_number_real_with_invalid_utf8_after_e.json": "n number real with invalid utf8 after e" "n_number_real_without_fractional_part.json": "n number real without fractional part" "n_number_starting_with_dot.json": "n number starting with dot" "n_number_with_alpha.json": "n number with alpha" "n_number_with_alpha_char.json": "n number with alpha char" "n_number_with_leading_zero.json": "n number with leading zero" "n_object_bad_value.json": "n object bad value" "n_object_bracket_key.json": "n object bracket key" "n_object_comma_instead_of_colon.json": "n object comma instead of colon" "n_object_double_colon.json": "n object double colon" "n_object_emoji.json": "n object emoji" "n_object_garbage_at_end.json": "n object garbage at end" "n_object_key_with_single_quotes.json": "n object key with single quotes" "n_object_missing_colon.json": "n object missing colon" "n_object_missing_key.json": "n object missing key" "n_object_missing_semicolon.json": "n object missing semicolon" "n_object_missing_value.json": "n object missing value" "n_object_no-colon.json": "n object no-colon" "n_object_non_string_key.json": "n object non string key" "n_object_non_string_key_but_huge_number_instead.json": "n object non string key but huge number instead" "n_object_pi_in_key_and_trailing_comma.json": "n object pi in key and trailing comma" "n_object_repeated_null_null.json": "n object repeated null null" "n_object_several_trailing_commas.json": "n object several trailing commas" "n_object_single_quote.json": "n object single quote" "n_object_trailing_comma.json": "n object trailing comma" "n_object_trailing_comment.json": "n object trailing comment" "n_object_trailing_comment_open.json": "n object trailing comment open" "n_object_trailing_comment_slash_open.json": "n object trailing comment slash open" "n_object_trailing_comment_slash_open_incomplete.json": "n object trailing comment slash open incomplete" "n_object_two_commas_in_a_row.json": "n object two commas in a row" "n_object_unquoted_key.json": "n object unquoted key" "n_object_unterminated-value.json": "n object unterminated-value" "n_object_with_single_string.json": "n object with single string" "n_object_with_trailing_garbage.json": "n object with trailing garbage" "n_single_space.json": "n single space" "n_string_1_surrogate_then_escape.json": "n string 1 surrogate then escape" "n_string_1_surrogate_then_escape_u.json": "n string 1 surrogate then escape u" "n_string_1_surrogate_then_escape_u1.json": "n string 1 surrogate then escape u1" "n_string_1_surrogate_then_escape_u1x.json": "n string 1 surrogate then escape u1x" "n_string_accentuated_char_no_quotes.json": "n string accentuated char no quotes" "n_string_backslash_00.json": "n string backslash 00" "n_string_escape_x.json": "n string escape x" "n_string_escaped_backslash_bad.json": "n string escaped backslash bad" "n_string_escaped_ctrl_char_tab.json": "n string escaped ctrl char tab" "n_string_escaped_emoji.json": "n string escaped emoji" "n_string_incomplete_escape.json": "n string incomplete escape" "n_string_incomplete_escaped_character.json": "n string incomplete escaped character" "n_string_incomplete_surrogate.json": "n string incomplete surrogate" "n_string_incomplete_surrogate_escape_invalid.json": "n string incomplete surrogate escape invalid" "n_string_invalid-utf-8-in-escape.json": "n string invalid-utf-8-in-escape" "n_string_invalid_backslash_esc.json": "n string invalid backslash esc" "n_string_invalid_unicode_escape.json": "n string invalid unicode escape" "n_string_invalid_utf8_after_escape.json": "n string invalid utf8 after escape" "n_string_leading_uescaped_thinspace.json": "n string leading uescaped thinspace" "n_string_no_quotes_with_bad_escape.json": "n string no quotes with bad escape" "n_string_single_doublequote.json": "n string single doublequote" "n_string_single_quote.json": "n string single quote" "n_string_single_string_no_double_quotes.json": "n string single string no double quotes" "n_string_start_escape_unclosed.json": "n string start escape unclosed" "n_string_unescaped_crtl_char.json": "n string unescaped crtl char" "n_string_unescaped_newline.json": "n string unescaped newline" "n_string_unescaped_tab.json": "n string unescaped tab" "n_string_unicode_CapitalU.json": "n string unicode CapitalU" "n_string_with_trailing_garbage.json": "n string with trailing garbage" "n_structure_U+2060_word_joined.json": "n structure U+2060 word joined" "n_structure_UTF8_BOM_no_data.json": "n structure UTF8 BOM no data" "n_structure_angle_bracket_..json": "n structure angle bracket ." "n_structure_angle_bracket_null.json": "n structure angle bracket null" "n_structure_array_trailing_garbage.json": "n structure array trailing garbage" "n_structure_array_with_extra_array_close.json": "n structure array with extra array close" "n_structure_array_with_unclosed_string.json": "n structure array with unclosed string" "n_structure_ascii-unicode-identifier.json": "n structure ascii-unicode-identifier" "n_structure_capitalized_True.json": "n structure capitalized True" "n_structure_close_unopened_array.json": "n structure close unopened array" "n_structure_comma_instead_of_closing_brace.json": "n structure comma instead of closing brace" "n_structure_double_array.json": "n structure double array" "n_structure_end_array.json": "n structure end array" "n_structure_incomplete_UTF8_BOM.json": "n structure incomplete UTF8 BOM" "n_structure_lone-invalid-utf-8.json": "n structure lone-invalid-utf-8" "n_structure_lone-open-bracket.json": "n structure lone-open-bracket" "n_structure_no_data.json": "n structure no data" "n_structure_null-byte-outside-string.json": "n structure null-byte-outside-string" "n_structure_number_with_trailing_garbage.json": "n structure number with trailing garbage" "n_structure_object_followed_by_closing_object.json": "n structure object followed by closing object" "n_structure_object_unclosed_no_value.json": "n structure object unclosed no value" "n_structure_object_with_comment.json": "n structure object with comment" "n_structure_object_with_trailing_garbage.json": "n structure object with trailing garbage" "n_structure_open_array_apostrophe.json": "n structure open array apostrophe" "n_structure_open_array_comma.json": "n structure open array comma" "n_structure_open_array_open_object.json": "n structure open array open object" "n_structure_open_array_open_string.json": "n structure open array open string" "n_structure_open_array_string.json": "n structure open array string" "n_structure_open_object.json": "n structure open object" "n_structure_open_object_close_array.json": "n structure open object close array" "n_structure_open_object_comma.json": "n structure open object comma" "n_structure_open_object_open_array.json": "n structure open object open array" "n_structure_open_object_open_string.json": "n structure open object open string" "n_structure_open_object_string_with_apostrophes.json": "n structure open object string with apostrophes" "n_structure_open_open.json": "n structure open open" "n_structure_single_eacute.json": "n structure single eacute" "n_structure_single_star.json": "n structure single star" "n_structure_trailing_#.json": "n structure trailing #" "n_structure_uescaped_LF_before_string.json": "n structure uescaped LF before string" "n_structure_unclosed_array.json": "n structure unclosed array" "n_structure_unclosed_array_partial_null.json": "n structure unclosed array partial null" "n_structure_unclosed_array_unfinished_false.json": "n structure unclosed array unfinished false" "n_structure_unclosed_array_unfinished_true.json": "n structure unclosed array unfinished true" "n_structure_unclosed_object.json": "n structure unclosed object" "n_structure_unicode-identifier.json": "n structure unicode-identifier" "n_structure_whitespace_U+2060_word_joiner.json": "n structure whitespace U+2060 word joiner" "n_structure_whitespace_formfeed.json": "n structure whitespace formfeed" "n_multiple_tokens.json": "concatenated tokens" "n_incomplete_null_token.json": "incomplete null token" gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_unclosed_with_object_inside.json0000644000175000017500000000000313661715457032125 0ustar nicolasnicolas[{}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_repeated_null_null.json0000644000175000017500000000002513661715457030406 0ustar nicolasnicolas{null:null,null:null}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_9.e+.json0000644000175000017500000000000613661715457025220 0ustar nicolasnicolas[9.e+]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_number_and_comma.json0000644000175000017500000000000413661715457027664 0ustar nicolasnicolas[1,]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_array_open_object.json0000644000175000017500000000000213661715457032024 0ustar nicolasnicolas[{gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_NaN.json0000644000175000017500000000000513661715457025225 0ustar nicolasnicolas[NaN]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_invalid_unicode_escape.json0000644000175000017500000000001213661715457031241 0ustar nicolasnicolas["\uqqqq"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_double_extra_comma.json0000644000175000017500000000000713661715457030232 0ustar nicolasnicolas["x",,]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_array_with_unclosed_string.json0000644000175000017500000000000613661715457032755 0ustar nicolasnicolas["asd]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_+1.json0000644000175000017500000000000413661715457024763 0ustar nicolasnicolas[+1]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_lone-invalid-utf-8.json0000644000175000017500000000000113661715457030637 0ustar nicolasnicolasågnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_incomplete.json0000644000175000017500000000000413661715457026535 0ustar nicolasnicolas["x"gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_angle_bracket_null.json0000644000175000017500000000001013661715457031130 0ustar nicolasnicolas[]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_inner_array_no_comma.json0000644000175000017500000000000613661715457030561 0ustar nicolasnicolas[3[4]]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_close_unopened_array.json0000644000175000017500000000000213661715457031516 0ustar nicolasnicolas1]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_whitespace_U+2060_word_joiner.json0000644000175000017500000000000513661715457032725 0ustar nicolasnicolas[â ]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_just_minus.json0000644000175000017500000000000313661715457026575 0ustar nicolasnicolas[-]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_unclosed_array.json0000644000175000017500000000000213661715457030330 0ustar nicolasnicolas[1gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_missing_value.json0000644000175000017500000000000513661715457027374 0ustar nicolasnicolas{"a":gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_single_quote.json0000644000175000017500000000002013661715457027262 0ustar nicolasnicolas['single quote']gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_uescaped_LF_before_string.json0000644000175000017500000000001213661715457032401 0ustar nicolasnicolas[\u000A""]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_unclosed_object.json0000644000175000017500000000001413661715457030463 0ustar nicolasnicolas{"asd":"asd"gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_single_space.json0000644000175000017500000000000113661715457025631 0ustar nicolasnicolas gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_incomplete_UTF8_BOM.json0000644000175000017500000000000413661715457030762 0ustar nicolasnicolasï»{}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_capitalized_True.json0000644000175000017500000000000613661715457030612 0ustar nicolasnicolas[True]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_1.0e-.json0000644000175000017500000000000713661715457025273 0ustar nicolasnicolas[1.0e-]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_incomplete_escaped_character.json0000644000175000017500000000001113661715457032423 0ustar nicolasnicolas["\u00A"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_invalid+-.json0000644000175000017500000000000713661715457026331 0ustar nicolasnicolas[0e+-1]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_starting_with_dot.json0000644000175000017500000000000613661715457030306 0ustar nicolasnicolas[.123]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_real_without_fractional_part.json0000644000175000017500000000000413661715457032506 0ustar nicolasnicolas[1.]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_unicode_CapitalU.json0000644000175000017500000000001013661715457027773 0ustar nicolasnicolas"\UA66D"gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_unclosed_trailing_comma.json0000644000175000017500000000000313661715457031256 0ustar nicolasnicolas[1,gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_with_single_string.json0000644000175000017500000000002613661715457030434 0ustar nicolasnicolas{ "foo" : "bar", "a" }gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_object_with_comment.json0000644000175000017500000000002413661715457031345 0ustar nicolasnicolas{"a":/*comment*/"b"}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_invalid-negative-real.json0000644000175000017500000000001513661715457030721 0ustar nicolasnicolas[-123.123foo]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_incomplete_null.json0000644000175000017500000000000513661715457026372 0ustar nicolasnicolas[nul]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_open.json0000644000175000017500000000002013661715457027300 0ustar nicolasnicolas["\{["\{["\{["\{gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_-.json0000644000175000017500000000000113661715457024701 0ustar nicolasnicolas-gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_1eE2.json0000644000175000017500000000000613661715457025246 0ustar nicolasnicolas[1eE2]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_invalid-utf-8-in-exponent.json0000644000175000017500000000000613661715457031403 0ustar nicolasnicolas[1e1å]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_1_surrogate_then_escape_u.json0000644000175000017500000000001413661715457031704 0ustar nicolasnicolas["\uD800\u"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_trailing_comment_slash_open.json0000644000175000017500000000001313661715457032274 0ustar nicolasnicolas{"a":"b"}//gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_garbage_at_end.json0000644000175000017500000000001513661715457027432 0ustar nicolasnicolas{"a":"a" 123}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_array_apostrophe.json0000644000175000017500000000000213661715457031721 0ustar nicolasnicolas['gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_-01.json0000644000175000017500000000000513661715457025046 0ustar nicolasnicolas[-01]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_missing_colon.json0000644000175000017500000000000713661715457027374 0ustar nicolasnicolas{"a" b}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_array_comma.json0000644000175000017500000000000213661715457030631 0ustar nicolasnicolas[,gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_a_invalid_utf8.json0000644000175000017500000000000413661715457027272 0ustar nicolasnicolas[aå]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_no_data.json0000644000175000017500000000000013661715457026721 0ustar nicolasnicolas././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_object_string_with_apostrophes.jsongnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_object_string_with_apostrophes.js0000644000175000017500000000000413661715457034322 0ustar nicolasnicolas{'a'gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_incomplete_surrogate.json0000644000175000017500000000001613661715457031023 0ustar nicolasnicolas["\uD834\uDd"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_extra_close.json0000644000175000017500000000000613661715457026710 0ustar nicolasnicolas["x"]]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_array_with_extra_array_close.json0000644000175000017500000000000413661715457033257 0ustar nicolasnicolas[1]]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_0.e1.json0000644000175000017500000000000613661715457025215 0ustar nicolasnicolas[0.e1]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_array_comma_after_close.json0000644000175000017500000000000513661715457030041 0ustar nicolasnicolas[""],gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_object_close_array.json0000644000175000017500000000000213661715457032170 0ustar nicolasnicolas{]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_infinity.json0000644000175000017500000000001213661715457026400 0ustar nicolasnicolas[Infinity]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_0_capital_E+.json0000644000175000017500000000000513661715457026724 0ustar nicolasnicolas[0E+]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_minus_infinity.json0000644000175000017500000000001313661715457027614 0ustar nicolasnicolas[-Infinity]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_unclosed_array_unfinished_true.json0000644000175000017500000000001413661715457033606 0ustar nicolasnicolas[ false, trugnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_trailing_comment_open.json0000644000175000017500000000001613661715457031105 0ustar nicolasnicolas{"a":"b"}/**//gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_1_000.json0000644000175000017500000000001113661715457025265 0ustar nicolasnicolas[1 000.0]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_invalid-utf-8-in-escape.json0000644000175000017500000000000713661715457031022 0ustar nicolasnicolas["\uå"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_trailing_comma.json0000644000175000017500000000001113661715457027511 0ustar nicolasnicolas{"id":0,}gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_null-byte-outside-string.json0000644000175000017500000000000313661715457032210 0ustar nicolasnicolas[]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_object_unclosed_no_value.json0000644000175000017500000000000413661715457032352 0ustar nicolasnicolas{"":gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_UTF8_BOM_no_data.json0000644000175000017500000000000313661715457030227 0ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_object_comma.json0000644000175000017500000000000213661715457030761 0ustar nicolasnicolas{,gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_1.0e+.json0000644000175000017500000000000713661715457025271 0ustar nicolasnicolas[1.0e+]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_number_-1.0..json0000644000175000017500000000000713661715457025204 0ustar nicolasnicolas[-1.0.]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_single_eacute.json0000644000175000017500000000000113661715457030124 0ustar nicolasnicolaségnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_unescaped_newline.json0000644000175000017500000000001413661715457030257 0ustar nicolasnicolas["new line"]gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_structure_open_object_open_array.json0000644000175000017500000000000213661715457032024 0ustar nicolasnicolas{[././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootgnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_trailing_comment_slash_open_incomplete.jsongnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_object_trailing_comment_slash_open_incomplete.js0000644000175000017500000000001213661715457034155 0ustar nicolasnicolas{"a":"b"}/gnatcoll-core-21.0.0/testsuite/tests/json/invalid/n_string_1_surrogate_then_escape_u1x.json0000644000175000017500000000001613661715457032157 0ustar nicolasnicolas["\uD800\u1x"]gnatcoll-core-21.0.0/testsuite/tests/json/api/0000755000175000017500000000000013743647711021117 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/json/api/test.adb0000644000175000017500000003554613743647711022563 0ustar nicolasnicolaswith Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with GNATCOLL.JSON; use GNATCOLL.JSON; with GNATCOLL.Strings; use GNATCOLL.Strings; with Test_Assert; function Test return Integer is package A renames Test_Assert; function Less (Left, Right : JSON_Value) return Boolean; -- Arbitrary and incomplete comparison function to test sorting facilities procedure Check_Image (Val : JSON_Value; Expected_Image : String); -- Assert that the compact image for Val is Expected_Image procedure Check_Error (Result : Read_Result; Expected_Error : String); -- Assert that Result comes from an unsuccessful read and compare its error -- information formatted by Format_Parsing_Error to Expected_Error. ----------------- -- Check_Image -- ----------------- procedure Check_Image (Val : JSON_Value; Expected_Image : String) is Img : constant String := Val.Write; begin A.Assert (Img = Expected_Image, "Check image: " & Img); end Check_Image; ----------------- -- Check_Error -- ----------------- procedure Check_Error (Result : Read_Result; Expected_Error : String) is Label : constant String := "Checking error: " & Expected_Error; begin A.Assert (not Result.Success, Label & " (parsing error)"); declare Error : constant String := Format_Parsing_Error (Result.Error); begin A.Assert (Error = Expected_Error, Label & " (error message)"); end; end Check_Error; ---------- -- Less -- ---------- function Less (Left, Right : JSON_Value) return Boolean is begin if Left.Kind /= Right.Kind then return Left.Kind < Right.Kind; end if; case Left.Kind is when JSON_Null_Type => return False; when JSON_Boolean_Type => return Boolean'(Left.Get) < Right.Get; when JSON_Int_Type => return Integer'(Left.Get) < Right.Get; when JSON_String_Type => return String'(Left.Get) < String'(Right.Get); when others => null; end case; return (raise Program_Error); end Less; Int_0 : JSON_Value := Create (Integer'(0)); Int_1 : constant JSON_Value := Create (Integer'(1)); Hello_World : constant JSON_Value := Create ("Hello world!"); Float_0 : constant JSON_Value := Create (Float'(0.0)); Float_1 : constant JSON_Value := Create (Float'(1.0)); Float_0_Image : constant String := Float_0.Write; Float_1_Image : constant String := Float_1.Write; Dummy_Int : Integer; begin -------------------- -- Array handling -- -------------------- declare Arr : JSON_Array := Int_0 & Int_1 & Hello_World; Empty : constant JSON_Array := Empty_Array; begin -- Inspect an empty array A.Assert (Is_Empty (Empty_Array)); A.Assert (Length (Empty_Array) = 0); begin Dummy_Int := Get (Get (Empty_Array, 1)); A.Assert (False, "Out-of-bound JSON_Array.Get"); exception when Constraint_Error => null; end; -- Inspect a 3-elements array A.Assert (not Is_Empty (Arr)); A.Assert (Length (Arr) = 3); A.Assert (Get (Arr, 1) = Int_0); A.Assert (Get (Arr, 2) = Int_1); A.Assert (Get (Arr, 3) = Hello_World); begin Dummy_Int := Get (Get (Empty_Array, 4)); A.Assert (False, "Out-of-bound JSON_Array.Get"); exception when Constraint_Error => null; end; -- Array modifications Append (Arr, Int_0); A.Assert (Length (Arr) = 4); A.Assert (Get (Arr, 4) = Int_0); Prepend (Arr, Hello_World); A.Assert (Length (Arr) = 5); A.Assert (Get (Arr, 1) = Hello_World); Set_Element (Arr, 1, Int_1); A.Assert (Length (Arr) = 5); A.Assert (Get (Arr, 1) = Int_1); Clear (Arr); A.Assert (Length (Arr) = 0); Arr := Hello_World & Int_0 & Int_1 & Int_0; Sort (Arr, Less'Access); Check_Image (Create (Arr), "[0,0,1,""Hello world!""]"); -- Array iteration declare I : Positive := 1; begin for V of Arr loop case I is when 1 => A.Assert (V = Int_0); when 2 => A.Assert (V = Int_0); when 3 => A.Assert (V = Int_1); when 4 => A.Assert (V = Hello_World); when others => A.Assert (False); end case; I := I + 1; end loop; for V of Empty loop A.Assert (False); end loop; end; end; ------------------------- -- JSON_Value.Is_Empty -- ------------------------- A.Assert (JSON_Null.Is_Empty); A.Assert (Create_Object.Is_Empty); A.Assert (Create (Empty_Array).Is_Empty); A.Assert (not Int_0.Is_Empty); A.Assert (not Hello_World.Is_Empty); ---------------------------------------------- -- Serialization/deserialization primitives -- ---------------------------------------------- -- Exception-based serialization/deserialization primitives get their own -- testing outside of this testcase. Check_Image (Read ("{}").Value, "{}"); Check_Image (Read (To_Unbounded_String ("{}")).Value, "{}"); Check_Error (Read ("{"), "1:2: empty stream"); Check_Error (Read (To_Unbounded_String ("{")), "1:2: empty stream"); ----------------------------- -- Creation of JSON values -- ----------------------------- Check_Image (Create, "null"); Check_Image (Create (True), "true"); Check_Image (Create (Integer'(0)), "0"); Check_Image (Create (Long_Integer'(1_000_000_000)), "1000000000"); Check_Image (Create (Long_Long_Integer'(10_000_000_000)), "10000000000"); Check_Image (Create (Float'(1.0)), Float_1_Image); Check_Image (Create (Long_Float'(1.0)), Float_1_Image); Check_Image (Create ("Hello world!"), """Hello world!"""); Check_Image (Create (To_Unbounded_String ("Hello world!")), """Hello world!"""); Check_Image (Create (To_XString ("Hello world!")), """Hello world!"""); Check_Image (Create (Empty_Array), "[]"); Check_Image (Create_Object, "{}"); ------------- -- Sorting -- ------------- declare Obj : JSON_Value := Create_Object; Arr : JSON_Value := Create (Int_1 & Int_0); begin Obj.Set_Field ("bar", Int_1); Obj.Set_Field ("foo", Int_0); Check_Image (Obj, "{""bar"":1,""foo"":0}"); Obj.Sort (Less'Access); Check_Image (Obj, "{""foo"":0,""bar"":1}"); Check_Image (Arr, "[1,0]"); Arr.Sort (Less'Access); Check_Image (Arr, "[0,1]"); Int_0.Sort (Less'Access); Check_Image (Int_0, "0"); end; ----------------------------------------- -- General modification of JSON values -- ----------------------------------------- declare Arr : constant JSON_Value := Create (Empty_Array); Cloned : JSON_Value; begin Check_Image (Arr, "[]"); Arr.Append (Int_0); Check_Image (Arr, "[0]"); Cloned := Arr.Clone; Cloned.Append (Int_1); Check_Image (Cloned, "[0,1]"); Check_Image (Arr, "[0]"); end; ------------------------------ -- Equality for JSON values -- ------------------------------ A.Assert (not (Create (Integer'(0)) = Create ("0"))); A.Assert (Create = Create); A.Assert (Int_0 = Create (Integer'(0))); A.Assert (Int_1 /= Create (Integer'(0))); A.Assert (Create (True) = Create (True)); A.Assert (Create (True) /= Create (False)); A.Assert (Create (Float'(1.0)) = Create (Float'(1.0))); A.Assert (Create (Float'(1.0)) /= Create (Float'(2.0))); A.Assert (Create ("Hello") = Create (To_XString ("Hello"))); A.Assert (Create ("Hello") /= Create ("world")); declare Arr : constant JSON_Value := Create (Int_0 & Int_1); begin A.Assert (Arr = Arr); A.Assert (Arr = Create (Int_0 & Int_1)); A.Assert (Arr /= Create (Int_0 & Int_1 & Int_0)); A.Assert (Arr /= Create (Int_1 & Int_0)); end; declare Obj_A : constant JSON_Value := Create_Object; Obj_A0 : constant JSON_Value := Create_Object; Obj_A1 : constant JSON_Value := Create_Object; Obj_B : constant JSON_Value := Create_Object; Obj_AB : constant JSON_Value := Create_Object; Obj_BA : constant JSON_Value := Create_Object; begin Obj_A.Set_Field ("A", Int_0); Obj_A0.Set_Field ("A", Int_0); Obj_A1.Set_Field ("A", Int_1); Obj_B.Set_Field ("B", Int_0); Obj_AB.Set_Field ("A", Int_0); Obj_AB.Set_Field ("B", Int_0); Obj_BA.Set_Field ("B", Int_0); Obj_BA.Set_Field ("A", Int_0); A.Assert (Obj_A = Obj_A); -- Same pointer A.Assert (Obj_A = Obj_A0); -- Same contents A.Assert (Obj_A /= Obj_A1); -- Different value A.Assert (Obj_A /= Obj_B); -- Different field name A.Assert (Obj_A /= Obj_AB); -- Different size A.Assert (Obj_AB = Obj_BA); -- Different order end; ---------------------------------- -- Modification of JSON objects -- ---------------------------------- declare Obj : JSON_Value := Create_Object; begin Check_Image (Obj, "{}"); Obj.Set_Field ("foo", Int_0); Check_Image (Obj, "{""foo"":0}"); Obj.Set_Field ("bar", Int_1); Check_Image (Obj, "{""foo"":0,""bar"":1}"); Obj.Set_Field ("foo", Int_1); Check_Image (Obj, "{""foo"":1,""bar"":1}"); Obj.Set_Field (To_XString ("foo"), Int_0); Check_Image (Obj, "{""foo"":0,""bar"":1}"); Obj := Create_Object; Check_Image (Obj, "{}"); Obj.Set_Field ("foo", True); Check_Image (Obj, "{""foo"":true}"); Obj.Set_Field ("foo", Integer'(0)); Check_Image (Obj, "{""foo"":0}"); Obj.Set_Field ("foo", Long_Integer'(1)); Check_Image (Obj, "{""foo"":1}"); Obj.Set_Field ("foo", Float'(0.0)); Check_Image (Obj, "{""foo"":" & Float_0_Image & "}"); Obj.Set_Field_Long_Float ("foo", Long_Float'(1.0)); Check_Image (Obj, "{""foo"":" & Float_1_Image & "}"); Obj.Set_Field ("foo", "Hello, world!"); Check_Image (Obj, "{""foo"":""Hello, world!""}"); Obj.Set_Field ("foo", To_Unbounded_String ("Bye")); Check_Image (Obj, "{""foo"":""Bye""}"); Obj.Set_Field ("foo", Empty_Array); Check_Image (Obj, "{""foo"":[]}"); Obj := Create_Object; Obj.Set_Field_If_Not_Empty ("foo", "bar"); Check_Image (Obj, "{""foo"":""bar""}"); Obj.Set_Field_If_Not_Empty ("foo", ""); Check_Image (Obj, "{""foo"":""bar""}"); Obj := Create_Object; Obj.Set_Field_If_Not_Empty ("foo", To_Unbounded_String ("bar")); Check_Image (Obj, "{""foo"":""bar""}"); Obj.Set_Field_If_Not_Empty ("foo", To_Unbounded_String ("")); Check_Image (Obj, "{""foo"":""bar""}"); Obj := Create_Object; Obj.Set_Field_If_Not_Empty ("foo", Int_0 & Int_1); Check_Image (Obj, "{""foo"":[0,1]}"); Obj.Set_Field_If_Not_Empty ("foo", Empty_Array); Check_Image (Obj, "{""foo"":[0,1]}"); Obj.Set_Field ("bar", Integer'(1)); Check_Image (Obj, "{""foo"":[0,1],""bar"":1}"); Obj.Unset_Field ("foo"); Check_Image (Obj, "{""bar"":1}"); Obj.Unset_Field ("foo"); Check_Image (Obj, "{""bar"":1}"); Obj.Unset_Field ("bar"); Check_Image (Obj, "{}"); end; ----------------- -- Conversions -- ----------------- A.Assert (Create.Kind = JSON_Null_Type); A.Assert (Create (True).Get = True); A.Assert (Int_1.Get = Integer'(1)); A.Assert (Int_1.Get = Long_Integer'(1)); A.Assert (Int_1.Get = Long_Long_Integer'(1)); A.Assert (Float_0.Get = Float'(0.0)); A.Assert (UTF8_String'(Hello_World.Get) = "Hello world!"); A.Assert (UTF8_Unbounded_String'(Hello_World.Get) = To_Unbounded_String ("Hello world!")); A.Assert (UTF8_XString'(Hello_World.Get) = To_XString ("Hello world!")); A.Assert (Create (Int_0 & Int_1).Get = Int_0 & Int_1); declare Obj : constant JSON_Value := Create_Object; begin Obj.Set_Field ("foo", Integer'(1)); A.Assert (Obj.Has_Field ("foo")); A.Assert (not Obj.Has_Field ("bar")); Obj.Set_Field ("foo", True); A.Assert (Obj.Get ("foo") = True); Obj.Set_Field ("foo", Int_1); A.Assert (Obj.Get ("foo") = Integer'(1)); A.Assert (Obj.Get ("foo") = Long_Integer'(1)); Obj.Set_Field ("foo", Float'(1.0)); A.Assert (Obj.Get ("foo") = Float'(1.0)); A.Assert (Obj.Get_Long_Float ("foo") = Long_Float'(1.0)); Obj.Set_Field ("foo", "Hello world!"); A.Assert (UTF8_String'(Obj.Get ("foo")) = "Hello world!"); A.Assert (UTF8_Unbounded_String'(Obj.Get ("foo")) = To_Unbounded_String ("Hello world!")); Obj.Set_Field ("foo", Empty_Array); A.Assert (Obj.Get ("foo") = Empty_Array); end; --------------- -- Iteration -- --------------- declare Obj : constant JSON_Value := Create_Object; Expected_Trace_1 : constant String := "foo: 0" & ASCII.LF & "bar: 1" & ASCII.LF; Expected_Trace_2 : constant String := "foo" & ASCII.LF & "bar" & ASCII.LF; type Iteration_Data (With_Value : Boolean) is record Result : Unbounded_String; case With_Value is when False => null; when True => Value_Separator : Character; end case; end record; Data_1 : Iteration_Data := (With_Value => True, Result => <>, Value_Separator => ':'); Data_2 : Iteration_Data := (With_Value => False, Result => <>); procedure Iterate (Data : in out Iteration_Data; Name : String; Value : JSON_Value); procedure Iterate (Name : String; Value : JSON_Value); ------------- -- Iterate -- ------------- procedure Iterate (Data : in out Iteration_Data; Name : String; Value : JSON_Value) is begin Append (Data.Result, Name); if Data.With_Value then Append (Data.Result, Data.Value_Separator & " " & String'(Value.Write)); end if; Append (Data.Result, ASCII.LF); end Iterate; procedure Iterate (Name : String; Value : JSON_Value) is begin Iterate (Data_1, Name, Value); end Iterate; procedure Map_JSON_Object is new Gen_Map_JSON_Object (Iteration_Data); begin Obj.Set_Field ("foo", Int_0); Obj.Set_Field ("bar", Int_1); Data_1.Result := Null_Unbounded_String; Obj.Map_JSON_Object (Iterate'Access); A.Assert (To_String (Data_1.Result), Expected_Trace_1, "Checking trace: " & Expected_Trace_1); Data_1.Result := Null_Unbounded_String; Map_JSON_Object (Obj, Iterate'Access, Data_1); A.Assert (To_String (Data_1.Result), Expected_Trace_1, "Checking trace: " & Expected_Trace_1); Map_JSON_Object (Obj, Iterate'Access, Data_2); A.Assert (To_String (Data_2.Result), Expected_Trace_2, "Checking trace: " & Expected_Trace_2); end; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/json/api/test.yaml0000644000175000017500000000004413661715457022762 0ustar nicolasnicolasdescription: Exhaustive API testing gnatcoll-core-21.0.0/testsuite/tests/json/validation/0000755000175000017500000000000013661715457022502 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_one-byte-utf-8.json0000644000175000017500000000001213661715457027767 0ustar nicolasnicolas["\u002c"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_1_2_3_bytes_UTF-8_sequences.json0000644000175000017500000000002613661715457032260 0ustar nicolasnicolas["\u0060\u012a\u12AB"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_empty_key.json0000644000175000017500000000000613661715457027255 0ustar nicolasnicolas{"":0}gnatcoll-core-21.0.0/testsuite/tests/json/validation/false.json0000644000175000017500000000000613661715457024463 0ustar nicolasnicolasfalse gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_structure_trailing_newline.json0000644000175000017500000000000613661715457031373 0ustar nicolasnicolas["a"] gnatcoll-core-21.0.0/testsuite/tests/json/validation/keyword_seq.json0000644000175000017500000000002413661715457025725 0ustar nicolasnicolas[true, false, null] gnatcoll-core-21.0.0/testsuite/tests/json/validation/test2.json0000644000175000017500000000000313661715457024427 0ustar nicolasnicolas[] gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_real_capital_e_neg_exp.json0000644000175000017500000000000613661715457031722 0ustar nicolasnicolas[1E-2]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_structure_whitespace_array.json0000644000175000017500000000000413661715457031371 0ustar nicolasnicolas [] gnatcoll-core-21.0.0/testsuite/tests/json/validation/basic_object.json0000644000175000017500000000011213661715457025776 0ustar nicolasnicolas{"a": 1, "b": "a tringg", "c": [1, 2, 3], "d": {"a": "a"}, "e": null} gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_duplicated_key_and_value.json0000644000175000017500000000002113661715457032250 0ustar nicolasnicolas{"a":"b","a":"b"}gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_simple_int.json0000644000175000017500000000000513661715457027433 0ustar nicolasnicolas[123]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_negative_int.json0000644000175000017500000000000613661715457027745 0ustar nicolasnicolas[-123]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unicode_U+FFFE_nonchar.json0000644000175000017500000000001213661715457031430 0ustar nicolasnicolas["\uFFFE"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_structure_lonely_false.json0000644000175000017500000000000513661715457030514 0ustar nicolasnicolasfalsegnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_real_capital_e.json0000644000175000017500000000000613661715457030215 0ustar nicolasnicolas[1E22]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_backslash_and_u_escaped_zero.json0000644000175000017500000000001313661715457033131 0ustar nicolasnicolas["\\u0000"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_with_leading_space.json0000644000175000017500000000000413661715457030726 0ustar nicolasnicolas [1]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_backslash_doublequotes.json0000644000175000017500000000000613661715457032035 0ustar nicolasnicolas["\""]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_structure_lonely_int.json0000644000175000017500000000000213661715457030211 0ustar nicolasnicolas42gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_u+2029_par_sep.json0000644000175000017500000000000713661715457027655 0ustar nicolasnicolas["
"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/numbers.json0000644000175000017500000000011213661715457025042 0ustar nicolasnicolas[0, 12345643, 1E+2, 1e-2, -41, -0, 1.000, 1E4, 1e21, 0.6E4, -6] gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_nonCharacterInUTF-8_U+10FFFF.json0000644000175000017500000000001013661715457032054 0ustar nicolasnicolas["ô¿¿"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_uEscape.json0000644000175000017500000000003413661715457026675 0ustar nicolasnicolas["\u0061\u30af\u30EA\u30b9"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_with_trailing_space.json0000644000175000017500000000000413661715457031134 0ustar nicolasnicolas[2] gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_escaped_control_character.json0000644000175000017500000000001213661715457032464 0ustar nicolasnicolas["\u0012"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_empty.json0000644000175000017500000000000213661715457026401 0ustar nicolasnicolas{}gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_double_escape_a.json0000644000175000017500000000000713661715457030402 0ustar nicolasnicolas["\\a"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/null.json0000644000175000017500000000000513661715457024342 0ustar nicolasnicolasnull gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_double_close_to_zero.json0000644000175000017500000000012413661715457031472 0ustar nicolasnicolas[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_minus_zero.json0000644000175000017500000000000413661715457027461 0ustar nicolasnicolas[-0]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_simple.json0000644000175000017500000000001013661715457026533 0ustar nicolasnicolas{"a":[]}gnatcoll-core-21.0.0/testsuite/tests/json/validation/true.json0000644000175000017500000000000513661715457024347 0ustar nicolasnicolastrue gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_simple_ascii.json0000644000175000017500000000001013661715457027743 0ustar nicolasnicolas["asd "]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_negative_one.json0000644000175000017500000000000413661715457027732 0ustar nicolasnicolas[-1]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_pi.json0000644000175000017500000000000613661715457025717 0ustar nicolasnicolas["Ï€"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_structure_lonely_negative_real.json0000644000175000017500000000000413661715457032226 0ustar nicolasnicolas-0.1gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unicodeEscapedBackslash.json0000644000175000017500000000001213661715457032033 0ustar nicolasnicolas["\u005C"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_extreme_numbers.json0000644000175000017500000000004313661715457030454 0ustar nicolasnicolas{ "min": -1.0e+28, "max": 1.0e+28 }gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_nonCharacterInUTF-8_U+FFFF.json0000644000175000017500000000000713661715457031721 0ustar nicolasnicolas["ï¿¿"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_space.json0000644000175000017500000000000313661715457026377 0ustar nicolasnicolas" "gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_structure_lonely_null.json0000644000175000017500000000000413661715457030373 0ustar nicolasnicolasnullgnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object.json0000644000175000017500000000003213661715457025166 0ustar nicolasnicolas{"asd":"sdf", "dfg":"fgh"}gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_0e+1.json0000644000175000017500000000000613661715457025731 0ustar nicolasnicolas[0e+1]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_last_surrogates_1_and_2.json0000644000175000017500000000002013661715457032007 0ustar nicolasnicolas["\uDBFF\uDFFF"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_real_pos_exponent.json0000644000175000017500000000000613661715457031015 0ustar nicolasnicolas[1e+2]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_accepted_surrogate_pairs.json0000644000175000017500000000003413661715457032351 0ustar nicolasnicolas["\ud83d\ude39\ud83d\udc8d"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_long_strings.json0000644000175000017500000000015413661715457027763 0ustar nicolasnicolas{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_string_unicode.json0000644000175000017500000000015613661715457030271 0ustar nicolasnicolas{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" }gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_ending_with_newline.json0000644000175000017500000000000513661715457031136 0ustar nicolasnicolas["a"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/test.adb0000644000175000017500000000305513661715457024134 0ustar nicolasnicolaswith Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Command_Line; with Ada.Text_IO; with GNATCOLL.JSON; with GNAT.OS_Lib; function Test return Integer is package JSON renames GNATCOLL.JSON; package IO renames Ada.Text_IO; function Read_File (Filename : String) return Unbounded_String; function Read_File (Filename : String) return Unbounded_String is use GNAT.OS_Lib; F : constant File_Descriptor := Open_Read (Filename, Binary); Expected_Bytes_Read : Integer; Bytes_Read : Integer; begin Expected_Bytes_Read := Integer (File_Length (F)); declare Buffer_Str : aliased String (1 .. Expected_Bytes_Read); begin Bytes_Read := Read (F, Buffer_Str'Address, Expected_Bytes_Read); pragma Assert (Bytes_Read = Expected_Bytes_Read); Close (F); return To_Unbounded_String (Buffer_Str); end; end Read_File; -- Read json filename passed as first argument Filename : constant String := Ada.Command_Line.Argument (1); JSON_Data : constant Unbounded_String := Read_File (Filename); begin -- Parse the json declare Value : constant JSON.JSON_Value := JSON.Read (Strm => JSON_Data, Filename => Filename); begin -- Dump JSON back to stdout for validation by the python json -- parser declare New_JSON_Data : constant Unbounded_String := JSON.Write (Item => Value, Compact => False); begin IO.Put_Line (To_String (New_JSON_Data)); end; end; return 0; end Test; gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_simple_real.json0000644000175000017500000000001413661715457027564 0ustar nicolasnicolas[123.456789]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unicode_U+FDD0_nonchar.json0000644000175000017500000000001213661715457031377 0ustar nicolasnicolas["\uFDD0"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unicode_U+10FFFE_nonchar.json0000644000175000017500000000002013661715457031570 0ustar nicolasnicolas["\uDBFF\uDFFE"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/string.json0000644000175000017500000000001613661715457024700 0ustar nicolasnicolas"Hello\n\r\t" gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unescaped_char_delete.json0000644000175000017500000000000513661715457031574 0ustar nicolasnicolas[""]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_real_fraction_exponent.json0000644000175000017500000000001413661715457032020 0ustar nicolasnicolas[123.456e78]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_u+2028_line_sep.json0000644000175000017500000000000713661715457030021 0ustar nicolasnicolas["
"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_comments.json0000644000175000017500000000002113661715457027131 0ustar nicolasnicolas["a/*b*/c/*d//e"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_real_exponent.json0000644000175000017500000000001013661715457030127 0ustar nicolasnicolas[123e45]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_duplicated_key.json0000644000175000017500000000002113661715457030232 0ustar nicolasnicolas{"a":"b","a":"c"}gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number.json0000644000175000017500000000001013661715457025204 0ustar nicolasnicolas[123e65]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_double_escape_n.json0000644000175000017500000000000713661715457030417 0ustar nicolasnicolas["\\n"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_uescaped_newline.json0000644000175000017500000000002113661715457030616 0ustar nicolasnicolas["new\u000Aline"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_int_with_exp.json0000644000175000017500000000000613661715457027772 0ustar nicolasnicolas[20e1]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_two-byte-utf-8.json0000644000175000017500000000001213661715457030017 0ustar nicolasnicolas["\u0123"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/test1.json0000644000175000017500000000000413661715457024427 0ustar nicolasnicolas{ } gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_null_escape.json0000644000175000017500000000001213661715457027576 0ustar nicolasnicolas["\u0000"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_0e1.json0000644000175000017500000000000513661715457025655 0ustar nicolasnicolas[0e1]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unicode_U+2064_invisible_plus.json0000644000175000017500000000001213661715457032714 0ustar nicolasnicolas["\u2064"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_negative_zero.json0000644000175000017500000000000413661715457030130 0ustar nicolasnicolas[-0]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_structure_lonely_string.json0000644000175000017500000000000513661715457030730 0ustar nicolasnicolas"asd"gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_structure_true_in_array.json0000644000175000017500000000000613661715457030704 0ustar nicolasnicolas[true]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_with_del_character.json0000644000175000017500000000000713661715457031123 0ustar nicolasnicolas["aa"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_escaped_null_in_key.json0000644000175000017500000000002413661715457031243 0ustar nicolasnicolas{"foo\u0000bar": 42}gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_with_1_and_newline.json0000644000175000017500000000000413661715457030653 0ustar nicolasnicolas[1 ]gnatcoll-core-21.0.0/testsuite/tests/json/validation/test.yaml0000644000175000017500000001467013661715457024355 0ustar nicolasnicolasdriver: 'json_validation' data_files: test1.json: "Minimal test" test2.json: "Minimal test2" false.json: "null keyword" null.json: "false keyword" true.json: "true keyword" keyword_seq.json: "an array of null, false and true" basic_object.json: "a basic JSON object" string.json: "a string with some escape sequences" numbers.json: "an array of various kind of numbers" "y_array_arraysWithSpaces.json": "y array arraysWithSpaces" "y_array_empty-string.json": "y array empty-string" "y_array_empty.json": "y array empty" "y_array_ending_with_newline.json": "y array ending with newline" "y_array_false.json": "y array false" "y_array_heterogeneous.json": "y array heterogeneous" "y_array_null.json": "y array null" "y_array_with_1_and_newline.json": "y array with 1 and newline" "y_array_with_leading_space.json": "y array with leading space" "y_array_with_several_null.json": "y array with several null" "y_array_with_trailing_space.json": "y array with trailing space" "y_number.json": "y number" "y_number_0e+1.json": "y number 0e+1" "y_number_0e1.json": "y number 0e1" "y_number_after_space.json": "y number after space" "y_number_double_close_to_zero.json": "y number double close to zero" "y_number_int_with_exp.json": "y number int with exp" "y_number_minus_zero.json": "y number minus zero" "y_number_negative_int.json": "y number negative int" "y_number_negative_one.json": "y number negative one" "y_number_negative_zero.json": "y number negative zero" "y_number_real_capital_e.json": "y number real capital e" "y_number_real_capital_e_neg_exp.json": "y number real capital e neg exp" "y_number_real_capital_e_pos_exp.json": "y number real capital e pos exp" "y_number_real_exponent.json": "y number real exponent" "y_number_real_fraction_exponent.json": "y number real fraction exponent" "y_number_real_neg_exp.json": "y number real neg exp" "y_number_real_pos_exponent.json": "y number real pos exponent" "y_number_simple_int.json": "y number simple int" "y_number_simple_real.json": "y number simple real" "y_object.json": "y object" "y_object_basic.json": "y object basic" "y_object_duplicated_key.json": "y object duplicated key" "y_object_duplicated_key_and_value.json": "y object duplicated key and value" "y_object_empty.json": "y object empty" "y_object_empty_key.json": "y object empty key" "y_object_escaped_null_in_key.json": "y object escaped null in key" "y_object_extreme_numbers.json": "y object extreme numbers" "y_object_long_strings.json": "y object long strings" "y_object_simple.json": "y object simple" "y_object_string_unicode.json": "y object string unicode" "y_object_with_newlines.json": "y object with newlines" "y_string_1_2_3_bytes_UTF-8_sequences.json": "y string 1 2 3 bytes UTF-8 sequences" "y_string_accepted_surrogate_pair.json": "y string accepted surrogate pair" "y_string_accepted_surrogate_pairs.json": "y string accepted surrogate pairs" "y_string_allowed_escapes.json": "y string allowed escapes" "y_string_backslash_and_u_escaped_zero.json": "y string backslash and u escaped zero" "y_string_backslash_doublequotes.json": "y string backslash doublequotes" "y_string_comments.json": "y string comments" "y_string_double_escape_a.json": "y string double escape a" "y_string_double_escape_n.json": "y string double escape n" "y_string_escaped_control_character.json": "y string escaped control character" "y_string_escaped_noncharacter.json": "y string escaped noncharacter" "y_string_in_array.json": "y string in array" "y_string_in_array_with_leading_space.json": "y string in array with leading space" "y_string_last_surrogates_1_and_2.json": "y string last surrogates 1 and 2" "y_string_nbsp_uescaped.json": "y string nbsp uescaped" "y_string_nonCharacterInUTF-8_U+10FFFF.json": "y string nonCharacterInUTF-8 U+10FFFF" "y_string_nonCharacterInUTF-8_U+1FFFF.json": "y string nonCharacterInUTF-8 U+1FFFF" "y_string_nonCharacterInUTF-8_U+FFFF.json": "y string nonCharacterInUTF-8 U+FFFF" "y_string_null_escape.json": "y string null escape" "y_string_one-byte-utf-8.json": "y string one-byte-utf-8" "y_string_pi.json": "y string pi" "y_string_simple_ascii.json": "y string simple ascii" "y_string_space.json": "y string space" "y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json": "y string surrogates U+1D11E MUSICAL SYMBOL G CLEF" "y_string_three-byte-utf-8.json": "y string three-byte-utf-8" "y_string_two-byte-utf-8.json": "y string two-byte-utf-8" "y_string_u+2028_line_sep.json": "y string u+2028 line sep" "y_string_u+2029_par_sep.json": "y string u+2029 par sep" "y_string_uEscape.json": "y string uEscape" "y_string_uescaped_newline.json": "y string uescaped newline" "y_string_unescaped_char_delete.json": "y string unescaped char delete" "y_string_unicode.json": "y string unicode" "y_string_unicodeEscapedBackslash.json": "y string unicodeEscapedBackslash" "y_string_unicode_2.json": "y string unicode 2" "y_string_unicode_U+10FFFE_nonchar.json": "y string unicode U+10FFFE nonchar" "y_string_unicode_U+1FFFE_nonchar.json": "y string unicode U+1FFFE nonchar" "y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json": "y string unicode U+200B ZERO WIDTH SPACE" "y_string_unicode_U+2064_invisible_plus.json": "y string unicode U+2064 invisible plus" "y_string_unicode_U+FDD0_nonchar.json": "y string unicode U+FDD0 nonchar" "y_string_unicode_U+FFFE_nonchar.json": "y string unicode U+FFFE nonchar" "y_string_unicode_escaped_double_quote.json": "y string unicode escaped double quote" "y_string_utf8.json": "y string utf8" "y_string_with_del_character.json": "y string with del character" "y_structure_lonely_false.json": "y structure lonely false" "y_structure_lonely_int.json": "y structure lonely int" "y_structure_lonely_negative_real.json": "y structure lonely negative real" "y_structure_lonely_null.json": "y structure lonely null" "y_structure_lonely_string.json": "y structure lonely string" "y_structure_lonely_true.json": "y structure lonely true" "y_structure_string_empty.json": "y structure string empty" "y_structure_trailing_newline.json": "y structure trailing newline" "y_structure_true_in_array.json": "y structure true in array" "y_structure_whitespace_array.json": "y structure whitespace array" gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_nonCharacterInUTF-8_U+1FFFF.json0000644000175000017500000000001013661715457031774 0ustar nicolasnicolas["𛿿"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_accepted_surrogate_pair.json0000644000175000017500000000002013661715457032161 0ustar nicolasnicolas["\uD801\udc37"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_in_array.json0000644000175000017500000000000713661715457027114 0ustar nicolasnicolas["asd"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_arraysWithSpaces.json0000644000175000017500000000000713661715457030414 0ustar nicolasnicolas[[] ]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_after_space.json0000644000175000017500000000000413661715457027543 0ustar nicolasnicolas[ 4]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_structure_string_empty.json0000644000175000017500000000000213661715457030561 0ustar nicolasnicolas""././@LongLink0000644000000000000000000000015400000000000011603 Lustar rootrootgnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.jsongnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CL0000644000175000017500000000002013661715457032612 0ustar nicolasnicolas["\uD834\uDd1e"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_with_several_null.json0000644000175000017500000000002413661715457030645 0ustar nicolasnicolas[1,null,null,null,2]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_basic.json0000644000175000017500000000001513661715457026330 0ustar nicolasnicolas{"asd":"sdf"}gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_null.json0000644000175000017500000000000613661715457026071 0ustar nicolasnicolas[null]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unicode.json0000644000175000017500000000001213661715457026732 0ustar nicolasnicolas["\uA66D"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_escaped_noncharacter.json0000644000175000017500000000001213661715457031437 0ustar nicolasnicolas["\uFFFF"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_in_array_with_leading_space.json0000644000175000017500000000001013661715457032777 0ustar nicolasnicolas[ "asd"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_nbsp_uescaped.json0000644000175000017500000000002113661715457030117 0ustar nicolasnicolas["new\u00A0line"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_utf8.json0000644000175000017500000000001313661715457026173 0ustar nicolasnicolas["€ð„ž"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_allowed_escapes.json0000644000175000017500000000002413661715457030441 0ustar nicolasnicolas["\"\\\/\b\f\n\r\t"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_real_neg_exp.json0000644000175000017500000000000613661715457027721 0ustar nicolasnicolas[1e-2]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_object_with_newlines.json0000644000175000017500000000001413661715457030125 0ustar nicolasnicolas{ "a": "b" }gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unicode_escaped_double_quote.json0000644000175000017500000000001213661715457033165 0ustar nicolasnicolas["\u0022"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unicode_2.json0000644000175000017500000000001513661715457027156 0ustar nicolasnicolas["â‚㈴â‚"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_heterogeneous.json0000644000175000017500000000002213661715457027771 0ustar nicolasnicolas[null, 1, "1", {}]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_number_real_capital_e_pos_exp.json0000644000175000017500000000000613661715457031752 0ustar nicolasnicolas[1E+2]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_three-byte-utf-8.json0000644000175000017500000000001213661715457030315 0ustar nicolasnicolas["\u0821"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_false.json0000644000175000017500000000000713661715457026212 0ustar nicolasnicolas[false]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unicode_U+1FFFE_nonchar.json0000644000175000017500000000002013661715457031510 0ustar nicolasnicolas["\uD83F\uDFFE"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json0000644000175000017500000000001213661715457032326 0ustar nicolasnicolas["\u200B"]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_empty.json0000644000175000017500000000000213661715457026251 0ustar nicolasnicolas[]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_array_empty-string.json0000644000175000017500000000000413661715457027557 0ustar nicolasnicolas[""]gnatcoll-core-21.0.0/testsuite/tests/json/validation/y_structure_lonely_true.json0000644000175000017500000000000413661715457030400 0ustar nicolasnicolastruegnatcoll-core-21.0.0/testsuite/tests/opt_parse/0000755000175000017500000000000013661715457021373 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/opt_parse/simple/0000755000175000017500000000000013661715457022664 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/opt_parse/simple/test.adb0000644000175000017500000001156113661715457024317 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Text_IO; use Ada.Text_IO; with GNATCOLL.Opt_Parse; use GNATCOLL.Opt_Parse; with GNATCOLL.Strings; use GNATCOLL.Strings; with Test_Assert; function Test return Integer is package A renames Test_Assert; function "+" (Self : String) return XString renames To_XString; package Arg is Parser : Argument_Parser := Create_Argument_Parser (Help => "Run Libadalang name resolution on a number of files"); package Files is new Parse_Positional_Arg_List (Parser => Parser, Name => "files", Arg_Type => XString, Help => "The files to parse"); package Quiet is new Parse_Flag (Parser => Parser, Short => "-q", Long => "--quiet", Help => "Whether the tool should be quiet or not"); package Charset is new Parse_Option (Parser => Parser, Short => "-C", Long => "--charset", Arg_Type => XString, Help => "What charset to use for the analysis context. " & "Default is ""latin-1""", Default_Val => +"latin-1"); package Jobs is new Parse_Option (Parser => Parser, Short => "-j", Long => "--jobs", Arg_Type => Integer, Help => "Number of jobs", Default_Val => 1); package Scenario_Vars is new Parse_Option_List (Parser => Parser, Short => "-X", Long => "--scenario-variable", Arg_Type => XString, Help => "Scenario variables", Accumulate => True); end Arg; begin if Arg.Parser.Parse ((+"-C", +"utf-8", +"a", +"b", +"c", +"d")) then Put_Line ("Charset = " & To_String (Arg.Charset.Get)); for F of Arg.Files.Get loop if not Arg.Quiet.Get then Put_Line ("Got file " & To_String (F)); end if; end loop; A.Assert (True, "Parsing"); else A.Assert (False, "Parsing"); end if; -- Test erroneous option A.Assert (not Arg.Parser.Parse ((+"-D", +"utf-8", +"a", +"b")), "Invalid option"); -- Test option after list A.Assert (Arg.Parser.Parse ((+"-C", +"utf-8", +"a", +"b", +"--quiet")), "Flag after positional list"); A.Assert (Arg.Parser.Parse ((+"-j", +"12", +"a")), "Integer value"); A.Assert (not Arg.Parser.Parse ((+"-j", +"lol", +"a")), "Invalid integer value"); A.Assert (not Arg.Parser.Parse ((+"a", +"-j")), "Incomplete option"); A.Assert (Arg.Parser.Help /= "", "Printing help"); if Arg.Parser.Parse ((+"--quiet", +"a", +"b")) then A.Assert (Arg.Quiet.Get, "Get flag with explicit val"); A.Assert (Arg.Charset.Get = "latin-1", "Get option with default val"); A.Assert (Arg.Jobs.Get = 1, "Check default job value"); else A.Assert (False); end if; -- Test accumulate mode of option list if Arg.Parser.Parse ((+"-C", +"utf-8", +"a", +"-Xa=b", +"-Xc=d")) then A.Assert (Arg.Scenario_Vars.Get (1) = "a=b"); A.Assert (Arg.Scenario_Vars.Get (2) = "c=d"); else A.Assert (False); end if; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/opt_parse/simple/test.yaml0000644000175000017500000000006413661715457024527 0ustar nicolasnicolasdescription: Simple test for GNATCOLL.Opt_Parse gnatcoll-core-21.0.0/testsuite/tests/opt_parse/disabled/0000755000175000017500000000000013661715457023142 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/opt_parse/disabled/test.adb0000644000175000017500000001062213661715457024572 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Opt_Parse; use GNATCOLL.Opt_Parse; with GNATCOLL.Strings; use GNATCOLL.Strings; with Test_Assert; function Test return Integer is package A renames Test_Assert; function "+" (Self : String) return XString renames To_XString; package Arg is Parser : Argument_Parser := Create_Argument_Parser (Help => "Test program"); package Flag is new Parse_Flag (Parser => Parser, Short => "-f", Long => "--flag", Help => "Test flag", Enabled => False); package Option is new Parse_Option (Parser => Parser, Short => "-o", Long => "--option", Arg_Type => XString, Help => "Test option", Default_Val => +"default", Enabled => False); package Option_List is new Parse_Option_List (Parser => Parser, Short => "-O", Long => "--option-list", Arg_Type => XString, Help => "Test option list", Accumulate => True, Enabled => False); package Positional is new Parse_Positional_Arg (Parser => Parser, Name => "positional", Help => "Test positional argument", Arg_Type => XString, Enabled => False); package Empty_Positional_List is new Parse_Positional_Arg_List (Parser => Parser, Name => "empty-positional-list", Help => "Test empty positional list argument", Allow_Empty => True, Arg_Type => XString, Enabled => False); package Positional_List is new Parse_Positional_Arg_List (Parser => Parser, Name => "positional-list", Help => "Test positional list argument", Allow_Empty => False, Arg_Type => XString, Enabled => False); end Arg; use type Arg.Option_List.Result_Array; use type Arg.Empty_Positional_List.Result_Array; begin A.Assert (Arg.Parser.Parse ((1 .. 0 => <>))); A.Assert (not Arg.Flag.Get); A.Assert (Arg.Option.Get = +"default"); A.Assert (Arg.Option_List.Get = (1 .. 0 => <>)); begin declare Dummy : constant XString := Arg.Positional.Get; begin A.Assert (False); end; exception when Disabled_Error => null; end; A.Assert (Arg.Empty_Positional_List.Get = (1 .. 0 => <>)); begin declare Dummy : constant Arg.Positional_List.Result_Array := Arg.Positional_List.Get; begin A.Assert (False); end; exception when Disabled_Error => null; end; return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/opt_parse/disabled/test.yaml0000644000175000017500000000012313661715457025001 0ustar nicolasnicolasdescription: Test the effect of the `Enabled => False` argument parser formal. gnatcoll-core-21.0.0/testsuite/tests/terminal/0000755000175000017500000000000013661715457021212 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/terminal/test.adb0000644000175000017500000001075013661715457022644 0ustar nicolasnicolas------------------------------------------------------------------------------ -- -- -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Terminal; use GNATCOLL.Terminal; with Ada.Text_IO; use Ada.Text_IO; with Test_Assert; function Test return Integer is Info : Terminal_Info; procedure Header (Name : String; Fg : ANSI_Color); -- Displays the table header cell using color Fg procedure Show (Name : String; Bg : ANSI_Color); -- Displays the table body cell using background color Bg procedure Put_No_Overflow (Str : String); -- Displays the string, but truncates it to the width of the screen ------------ -- Header -- ------------ procedure Header (Name : String; Fg : ANSI_Color) is begin Info.Set_Color (Standard_Output, Fg, Reset, Normal); Put (Name); end Header; --------------------- -- Put_No_Overflow -- --------------------- procedure Put_No_Overflow (Str : String) is Width : constant Integer := Info.Get_Width; begin if Width = -1 then Put (Str); else Put (Str (Str'First .. Integer'Min (Str'Last, Width))); end if; end Put_No_Overflow; ---------- -- Show -- ---------- procedure Show (Name : String; Bg : ANSI_Color) is begin Info.Set_Color (Standard_Output, Reset, Bg, Normal); Put (Name); for Fg in Black .. Grey loop Info.Set_Color (Standard_Output, Fg, Bg, Normal); Put ("X "); Info.Set_Color (Standard_Output, Style => Dim); Put ("X "); Info.Set_Color (Standard_Output, Style => Bright); Put ("X "); Info.Set_Color (Standard_Output, Style => Reset_All); Put (" "); end loop; New_Line; end Show; begin Info.Init_For_Stdout (Auto); Header (" ", Reset); Header ("black ", Black); Header ("red ", Red); Header ("green ", Green); Header ("yellow ", Yellow); Header ("blue ", Blue); Header ("magenta", Magenta); Header ("cyan ", Cyan); Header ("white ", Grey); New_Line; Show ("black ", Black); Show ("red ", Red); Show ("green ", Green); Show ("yellow ", Yellow); Show ("blue ", Blue); Show ("magenta ", Magenta); Show ("cyan ", Cyan); Show ("white ", Grey); for J in 1 .. 50 loop if J mod 10 = 0 then Put_No_Overflow ("Processing file" & J'Img & " with long name"); elsif J mod 5 = 0 then Put_No_Overflow ("Some very long string, in theory should be larger than the " & "actual size of the terminal, although some people use large " & "windows"); else Put ("Processing file" & J'Img); end if; delay 0.001; Info.Beginning_Of_Line; Info.Clear_To_End_Of_Line; end loop; return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/terminal/test.yaml0000644000175000017500000000016413661715457023056 0ustar nicolasnicolasdescription: Basic test for GNATCOLL.Terminal. As the effect is visual only, this only serves as a robustness test. gnatcoll-core-21.0.0/testsuite/tests/promises/0000755000175000017500000000000013661715457021240 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/promises/test.out0000644000175000017500000000077113661715457022755 0ustar nicolasnicolas=== Create chain Resolving... Convert_Int.On_Next input= 2 Convert_Float.On_Next input= 2.00000E+00 Display_String.On_Next input=value was 2.00000E+00 Display_String.On_Next input=value was 2.00000E+00 Display_Int.On_Next input= 2 === Create chain, will Fail Failing... Display_String.Failed because Explicit failure === Create chain, will Fail in middle Convert_Int.On_Next input= 3 Fail_On_Float: mark output as failed Display_String.Failed because explicit Display_String.Failed because explicit gnatcoll-core-21.0.0/testsuite/tests/promises/test.adb0000644000175000017500000000671113661715457022674 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Text_IO; use Ada.Text_IO; with GNATCOLL.Promises; use GNATCOLL.Promises; with Test_Promises_Support; use Test_Promises_Support; with Test_Assert; function Test return Integer is package A renames Test_Assert; use Int_To_Float, Float_To_Str, Str_Promises; function Get_Promise return Int_Promises.Promise; -- Dummy function function Get_Promise return Int_Promises.Promise is P : constant Int_Promises.Promise := Int_Promises.Create; begin -- ??? Could resolve in a task for instance return P; end Get_Promise; P : Int_Promises.Promise; begin pragma Warnings (Off, "use of an anonymous access type allocator"); Put_Line ("=== Create chain"); P := Get_Promise; Subscribe (P and (new Convert_Int & new Display_Int) and new Convert_Float and (new Display_String & new Display_String)); Put_Line ("Resolving..."); Baseline := 2; P.Set_Value (2); A.Assert (A.Assert_Count, 5, "expected number of asserts #1"); Put_Line ("=== Create chain, will Fail"); P := Get_Promise; Subscribe (P and new Convert_Int and new Convert_Float and new Display_String); Put_Line ("Failing..."); Message := new String'("Explicit failure"); P.Set_Error ("Explicit failure"); A.Assert (A.Assert_Count, 7, "expected number of asserts #2"); Put_Line ("=== Create chain, will Fail in middle"); P := Get_Promise; Subscribe (P and new Convert_Int and new Fail_On_Float and (new Display_String & new Display_String)); Baseline := 3; Message := new String'("explicit"); P.Set_Value (3); A.Assert (A.Assert_Count, 11, "expected number of asserts #3"); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/promises/test_promises_support.adb0000644000175000017500000000722613661715457026413 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Text_IO; use Ada.Text_IO; with Test_Assert; package body Test_Promises_Support is package A renames Test_Assert; overriding procedure On_Next (Self : in out Convert_Int; P : Integer; Output : in out Float_Promises.Promise) is pragma Unreferenced (Self); begin Put_Line ("Convert_Int.On_Next input=" & P'Img); A.Assert (P, Baseline, "expected input"); Output.Set_Value (Float (P)); end On_Next; overriding procedure On_Next (Self : in out Convert_Float; P : Float; Output : in out Str_Promises.Promise) is pragma Unreferenced (Self); begin Put_Line ("Convert_Float.On_Next input=" & P'Img); A.Assert (Integer (P), Baseline, "expected input"); Output.Set_Value ("value was" & P'Img); end On_Next; overriding procedure On_Next (Self : in out Display_Int; P : Integer) is pragma Unreferenced (Self); begin Put_Line ("Display_Int.On_Next input=" & P'Img); A.Assert (P, Baseline, "expected input"); end On_Next; overriding procedure On_Next (Self : in out Display_String; P : String) is pragma Unreferenced (Self); begin Put_Line ("Display_String.On_Next input=" & P); A.Assert (P, "value was 2.00000E+00", "expected input"); end On_Next; overriding procedure On_Error (Self : in out Display_String; Reason : String) is pragma Unreferenced (Self); begin Put_Line ("Display_String.Failed because " & Reason); A.Assert (Reason, Message.all, "expected reason"); end On_Error; overriding procedure On_Next (Self : in out Fail_On_Float; P : Float; Output : in out Str_Promises.Promise) is pragma Unreferenced (Self, P); begin Put_Line ("Fail_On_Float: mark output as failed"); Output.Set_Error ("explicit"); end On_Next; end Test_Promises_Support; gnatcoll-core-21.0.0/testsuite/tests/promises/test.yaml0000644000175000017500000000005713661715457023105 0ustar nicolasnicolasdescription: Basic test for GNATCOLL.Promises. gnatcoll-core-21.0.0/testsuite/tests/promises/test_promises_support.ads0000644000175000017500000000640013661715457026425 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Promises; use GNATCOLL.Promises; package Test_Promises_Support is Baseline : Integer := 0; Message : access String; -- Values that Assert routines should verify package Int_Promises is new Promises (Integer); package Float_Promises is new Promises (Float); package Str_Promises is new Promises (String); package Int_To_Float is new Chains (Int_Promises, Float_Promises); package Float_To_Str is new Chains (Float_Promises, Str_Promises); type Convert_Int is new Int_To_Float.Callback with null record; overriding procedure On_Next (Self : in out Convert_Int; P : Integer; Output : in out Float_Promises.Promise); type Convert_Float is new Float_To_Str.Callback with null record; overriding procedure On_Next (Self : in out Convert_Float; P : Float; Output : in out Str_Promises.Promise); type Display_String is new Str_Promises.Callback with null record; overriding procedure On_Next (Self : in out Display_String; P : String); overriding procedure On_Error (Self : in out Display_String; Reason : String); type Display_Int is new Int_Promises.Callback with null record; overriding procedure On_Next (Self : in out Display_Int; P : Integer); type Fail_On_Float is new Float_To_Str.Callback with null record; overriding procedure On_Next (Self : in out Fail_On_Float; P : Float; Output : in out Str_Promises.Promise); -- Always fails Output end Test_Promises_Support; gnatcoll-core-21.0.0/testsuite/tests/scripts/0000755000175000017500000000000013661715457021066 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/scripts/completion/0000755000175000017500000000000013743647711023235 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/scripts/completion/support.ads0000644000175000017500000000034313661715457025444 0ustar nicolasnicolaswith GNATCOLL.Scripts; package Support is procedure Register_Functions (Repo : GNATCOLL.Scripts.Scripts_Repository); -- Register the various scripting languages and the functions we export -- to them end Support; gnatcoll-core-21.0.0/testsuite/tests/scripts/completion/testconsole.ads0000644000175000017500000000227613661715457026301 0ustar nicolasnicolaswith GNATCOLL.Scripts; package TestConsole is type Test_Console is new GNATCOLL.Scripts.Virtual_Console_Record with private; overriding procedure Insert_Text (Console : access Test_Console; Txt : String); overriding procedure Insert_Prompt (Console : access Test_Console; Txt : String); overriding procedure Insert_Error (Console : access Test_Console; Txt : String); overriding procedure Insert_Log (Console : access Test_Console; Txt : String); overriding procedure Set_Data_Primitive (Instance : GNATCOLL.Scripts.Class_Instance; Console : access Test_Console); overriding function Get_Instance (Script : access GNATCOLL.Scripts.Scripting_Language_Record'Class; Console : access Test_Console) return GNATCOLL.Scripts.Class_Instance; overriding function Read (Console : access Test_Console; Size : Integer; Whole_Line : Boolean) return String; procedure Free (Console : in out Test_Console); -- Free memory associated with Console private type Test_Console is new GNATCOLL.Scripts.Virtual_Console_Record with record Instances : GNATCOLL.Scripts.Instance_List; end record; end TestConsole; gnatcoll-core-21.0.0/testsuite/tests/scripts/completion/support.adb0000644000175000017500000001002513661715457025421 0ustar nicolasnicolaswith GNATCOLL.Scripts; use GNATCOLL.Scripts; package body Support is procedure On_Hello (Data : in out Callback_Data'Class; Command : String); procedure On_Print_Float (Data : in out Callback_Data'Class; Command : String); procedure C1_Handler (Data : in out Callback_Data'Class; Command : String); procedure Custom_List_Handler (Data : in out Callback_Data'Class; Command : String); Prop : Integer := 0; Ro_Prop : constant Integer := 0; pragma Warnings (Off); Wo_Prop : Integer := 0; pragma Warnings (On); -- Imagine these are fields of a class. We simplify the test case by -- using global variables -------------------- -- On_Print_Float -- -------------------- procedure On_Print_Float (Data : in out Callback_Data'Class; Command : String) is pragma Unreferenced (Command); Param : constant Float := Nth_Arg (Data, 1); begin Set_Return_Value (Data, Param + 1.0); end On_Print_Float; -------------- -- On_Hello -- -------------- procedure On_Hello (Data : in out Callback_Data'Class; Command : String) is pragma Unreferenced (Command); begin Set_Return_Value (Data, "Hello " & Nth_Arg (Data, 1, "world") & " !"); end On_Hello; ------------------------- -- Custom_List_Handler -- ------------------------- procedure Custom_List_Handler (Data : in out Callback_Data'Class; Command : String) is begin Set_Return_Value (Data, "Executing command '" & Command & "'"); end Custom_List_Handler; ---------------- -- C1_Handler -- ---------------- procedure C1_Handler (Data : in out Callback_Data'Class; Command : String) is -- This could also be kept as a variable somewhere, but fetching it -- is relatively cheap C1 : constant Class_Type := New_Class (Get_Repository (Data), "C1"); Inst : Class_Instance; begin if Command = Constructor_Method then Inst := Nth_Arg (Data, 1, C1); Set_Data (Inst, C1, Integer'(Nth_Arg (Data, 2))); Set_Property (Inst, "id", 2); Set_Property (Inst, "name", "the_name"); elsif Command = "method" then Inst := Nth_Arg (Data, 1, C1); Set_Return_Value (Data, "Method applied to class" & Integer'Image (Get_Data (Inst, C1)) & " with param " & Nth_Arg (Data, 2)); elsif Command = "prop" then if Number_Of_Arguments (Data) = 1 then Set_Return_Value (Data, Prop); else Prop := Nth_Arg (Data, 2) + 1; -- Offset to make sure we were here end if; elsif Command = "ro_prop" then Set_Return_Value (Data, Ro_Prop); elsif Command = "wo_prop" then Wo_Prop := Nth_Arg (Data, 2); end if; end C1_Handler; ------------------------ -- Register_Functions -- ------------------------ procedure Register_Functions (Repo : Scripts_Repository) is C1 : Class_Type; Custom_List : constant Class_Type := New_Class (Repo, "MyList", Lookup_Class (Repo, "list")); begin Register_Command (Repo, "hello", 0, 1, Handler => On_Hello'Access); Register_Command (Repo, "print_float", 1, 1, Handler => On_Print_Float'Access); C1 := New_Class (Repo, "C1"); Register_Command (Repo, Constructor_Method, 1, 1, Class => C1, Handler => C1_Handler'Access); Register_Command (Repo, "method", 0, 1, Class => C1, Handler => C1_Handler'Access); Register_Property (Repo, "prop", Class => C1, Setter => C1_Handler'Access, Getter => C1_Handler'Access); Register_Property (Repo, "ro_prop", Class => C1, Getter => C1_Handler'Access); Register_Property (Repo, "wo_prop", Class => C1, Setter => C1_Handler'Access); Register_Command (Repo, "dump", Class => Custom_List, Handler => Custom_List_Handler'Access); end Register_Functions; end Support; gnatcoll-core-21.0.0/testsuite/tests/scripts/completion/test.adb0000644000175000017500000000316613661715457024674 0ustar nicolasnicolaswith Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with GNATCOLL.Scripts; use GNATCOLL.Scripts; with GNATCOLL.Scripts.Shell; use GNATCOLL.Scripts.Shell; with TestConsole; use TestConsole; with Support; with Test_Assert; function Test return Integer is package A renames Test_Assert; Repo : Scripts_Repository := new Scripts_Repository_Record; Console : aliased Test_Console; function Completions (Input : String; Lang : Scripting_Language) return String; function Completions (Input : String; Lang : Scripting_Language) return String is use String_Lists; Completions : String_Lists.List; Result : Unbounded_String; begin Complete (Lang, Input, Completions); for E of Completions loop Append (Result, E & ", "); end loop; return To_String (Result); end Completions; Sh : Scripting_Language; begin Register_Shell_Scripting (Repo); Register_Standard_Classes (Repo, "Console"); Support.Register_Functions (Repo); Sh := Lookup_Scripting_Language (Repo, "shell"); Set_Default_Console (Sh, Console'Unchecked_Access); A.Assert (Completions ("C1", Sh) = "C1, C1.method, ", "C1"); A.Assert (Completions ("", Sh) = "C1, C1.method, Console.clear, Console.flush, Console.isatty, " & "Console.read, Console.readline, Console.write, MyList.dump, " & "clear_cache, echo, echo_error, hello, load, print_float, ", """"""); A.Assert (Completions ("Ba", Sh) = "", "Ba"); Free (Console); Destroy (Repo); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/scripts/completion/test.yaml0000644000175000017500000000022713743647711025101 0ustar nicolasnicolasdescription: Completion test for GNATCOLL.Scripts and GNATCOLL.Scripts.Shell control: - [XFAIL, "env.valgrind", "Known memory leak: see S912-005"] gnatcoll-core-21.0.0/testsuite/tests/scripts/completion/testconsole.adb0000644000175000017500000000353513661715457026257 0ustar nicolasnicolaswith GNATCOLL.Scripts; use GNATCOLL.Scripts; with GNATCOLL.Traces; use GNATCOLL.Traces; package body TestConsole is Me : constant Trace_Handle := Create ("CONSOLE"); procedure Set_Data_Primitive (Instance : Class_Instance; Console : access Test_Console) is begin Set (Console.Instances, Instance); end Set_Data_Primitive; function Get_Instance (Script : access Scripting_Language_Record'Class; Console : access Test_Console) return Class_Instance is begin return Get (Console.Instances, Script); end Get_Instance; procedure Insert_Text (Console : access Test_Console; Txt : String) is pragma Unreferenced (Console); begin null; end Insert_Text; procedure Insert_Prompt (Console : access Test_Console; Txt : String) is pragma Unreferenced (Console, Txt); begin null; end Insert_Prompt; procedure Insert_Error (Console : access Test_Console; Txt : String) is pragma Unreferenced (Console); begin null; end Insert_Error; procedure Insert_Log (Console : access Test_Console; Txt : String) is pragma Unreferenced (Console); begin Trace (Me, Txt); end Insert_Log; procedure Free (Console : in out Test_Console) is begin Free (Console.Instances); end Free; function Read (Console : access Test_Console; Size : Integer; Whole_Line : Boolean) return String is pragma Unreferenced (Console); -- At most 20 characters Str : String (1 .. Integer'Min (20, Size)); Last : Integer := Str'Last; begin if Whole_Line then Str (Last) := ASCII.LF; Last := Last - 1; end if; for S in Str'First .. Last loop Str (S) := Character'Val (Character'Pos ('A') + S - Str'First); end loop; return Str; end Read; end TestConsole; gnatcoll-core-21.0.0/testsuite/tests/refcount/0000755000175000017500000000000013661715457021224 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/refcount/weak-race/0000755000175000017500000000000013743647711023061 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/refcount/weak-race/test.adb0000644000175000017500000000724313661715457024520 0ustar nicolasnicolaswith Ada.Exceptions; use Ada.Exceptions; with Ada.Real_Time; use Ada.Real_Time; with Ada.Task_Identification; with Ada.Text_IO; use Ada.Text_IO; with GNATCOLL.Refcount; with Test_Assert; use Test_Assert; function Test return Integer is package Shared_Holders is new GNATCOLL.Refcount.Shared_Pointers (Integer); use Shared_Holders; task type Weak_Tester is entry Take (Ptr : Ref); entry Stop; end Weak_Tester; -- This test runs three ref-to-weak and then weak-to-ref patterns in loops -- concurrently to check that both conversions are resilient to concurrent -- inc-ref/dec-ref operations. -- -- This is an attempt to check that there is no race condition between -- ref-counting drop to 0 happens and a weak-to-ref conversion. -- -- The structure of this test is the following: -- -- * Some code runs a weak-to-ref conversion at most Task_Inner_Range -- times, stopping as soon as the conversion returns a null reference. -- -- * Run three tasks (Weak_Tester) run that code Task_Outer_Range times. -- -- We expect race conditions to somehow make the weak-to-ref conversion -- produce a stale reference (which is invalid), so that dereferencing it -- would yield an unexpected number. subtype Task_Outer_Range is Positive range 1 .. 20_000; subtype Task_Inner_Range is Natural; Notification_Interval : constant Positive := 10_000; task body Weak_Tester is Stop_It : Boolean := False; W : Weak_Ref; begin Main_Loop : for J in Task_Outer_Range loop declare R : Ref; begin select accept Take (Ptr : Ref) do R := Ptr; end Take; or accept Stop do Stop_It := True; end Stop; end select; W := R.Weak; end; exit Main_Loop when Stop_It; for K in Task_Inner_Range'Range loop declare use Ada.Task_Identification; R : Ref; begin R.Set (W); if R = Null_Ref then if K = 0 then Put_Line ("Taken null at first time " & J'Img & ' ' & Image (Current_Task)); end if; if J mod Notification_Interval = 0 then Put_Line (J'Img & K'Img); end if; exit; end if; if R.Get /= J then Assert (False, "Expected " & J'Img & " took " & Integer'(R.Get)'Img); end if; end; end loop; end loop Main_Loop; exception when E : others => Assert (False, "Task " & Exception_Information (E)); end Weak_Tester; Test : array (1 .. 3) of Weak_Tester; Stamp : constant Time := Clock; begin for J in Task_Outer_Range loop declare R : Ref; begin R.Set (J); for J in Test'Range loop Test (J).Take (R); end loop; delay 0.00001; end; if To_Duration (Clock - Stamp) > 200.0 then Put_Line ("Too busy test machine, stop the test."); for J in Test'Range loop Test (J).Stop; end loop; exit; end if; end loop; return Test_Assert.Report; exception when E : others => Assert (False, "Main " & Exception_Information (E)); for J in Test'Range loop Test (J).Stop; end loop; return Test_Assert.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/refcount/weak-race/test.yaml0000644000175000017500000000042413743647711024724 0ustar nicolasnicolasdescription: Test for race between object disapearing and restore strong reference from weak reference. control: - [SKIP, 'env.valgrind', "Race condition never triggered with Valgrind, so no point in running the testcase for 300s in this mode." ] gnatcoll-core-21.0.0/testsuite/tests/refcount/nested/0000755000175000017500000000000013661715457022506 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/refcount/nested/test.adb0000644000175000017500000000375013661715457024142 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Refcount; with Test_Assert; function Test return Integer is package A renames Test_Assert; package Int_Refs is new GNATCOLL.Refcount.Shared_Pointers (Integer); R : Int_Refs.Ref; begin R.Set (12); A.Assert (R.Get = 12); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/refcount/nested/test.yaml0000644000175000017500000000014713661715457024353 0ustar nicolasnicolasdescription: Test for instantiation of GNATCOLL.Refcount.Shared pointers nested in a procedure gnatcoll-core-21.0.0/testsuite/tests/tribools/0000755000175000017500000000000013661715457021234 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/tribools/simple/0000755000175000017500000000000013661715457022525 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/tribools/simple/test.adb0000644000175000017500000000733213661715457024161 0ustar nicolasnicolaswith GNATCOLL.Tribooleans; use GNATCOLL.Tribooleans; with Test_Assert; function Test return Integer is package A renames Test_Assert; BT : constant Boolean := True; BF : constant Boolean := False; T : constant Triboolean := True; F : constant Triboolean := False; I : constant Triboolean := Indeterminate; begin ---------------- -- Equality -- ---------------- A.Assert (BT = T, "BT=T"); A.Assert (Equal (BT, T) = Triboolean'(True), "BT eq T"); A.Assert (not (BT = F), "BT=F"); A.Assert (Equal (BT, F) = Triboolean'(False), "BT eq F"); A.Assert (not (BT = I), "BT=I"); A.Assert (Equal (BT, I) = Indeterminate, "BT eq I"); A.Assert (not (BF = T), "BF=T"); A.Assert (Equal (BF, T) = Triboolean'(False), "BF eq F"); A.Assert (BF = F, "BF=F"); A.Assert (Equal (BF, F) = Triboolean'(True), "BF eq F"); A.Assert (not (BF = I), "BF=I"); A.Assert (Equal (BF, I) = Indeterminate, "BF eq I"); ----------- -- And -- ----------- A.Assert ((BF and F) = Triboolean'(False), "BF and F"); A.Assert ((BF and T) = Triboolean'(False), "BF and T"); A.Assert ((BF and I) = Triboolean'(False), "BF and I"); A.Assert ((BT and F) = Triboolean'(False), "BT and F"); A.Assert ((BT and T) = Triboolean'(True), "BT and T"); A.Assert ((BT and I) = Indeterminate, "BT and I"); A.Assert ((T and F) = Triboolean'(False), "T and F"); A.Assert ((T and T) = Triboolean'(True), "T and T"); A.Assert ((T and I) = Indeterminate, "T and I"); A.Assert ((F and F) = Triboolean'(False), "F and F"); A.Assert ((F and T) = Triboolean'(False), "F and T"); A.Assert ((F and I) = Triboolean'(False), "F and I"); A.Assert ((I and F) = Triboolean'(False), "I and F"); A.Assert ((I and T) = Indeterminate, "I and T"); A.Assert ((I and I) = Indeterminate, "I and I"); ---------- -- Or -- ---------- A.Assert ((BF or F) = Triboolean'(False), "BF or F"); A.Assert ((BF or T) = Triboolean'(True), "BF or T"); A.Assert ((BF or I) = Indeterminate, "BF or I"); A.Assert ((BT or F) = Triboolean'(True), "BT or F"); A.Assert ((BT or T) = Triboolean'(True), "BT or T"); A.Assert ((BT or I) = Triboolean'(True), "BT or I"); A.Assert ((T or F) = Triboolean'(True), "T or F"); A.Assert ((T or T) = Triboolean'(True), "T or T"); A.Assert ((T or I) = Triboolean'(True), "T or I"); A.Assert ((F or F) = Triboolean'(False), "F or F"); A.Assert ((F or T) = Triboolean'(True), "F or T"); A.Assert ((F or I) = Indeterminate, "F or I"); A.Assert ((I or F) = Indeterminate, "I or F"); A.Assert ((I or T) = Triboolean'(True), "I or T"); A.Assert ((I or I) = Indeterminate, "I or I"); ----------- -- Xor -- ----------- A.Assert ((BF xor F) = Triboolean'(False), "BF xor F"); A.Assert ((BF xor T) = Triboolean'(True), "BF xor T"); A.Assert ((BF xor I) = Indeterminate, "BF xor I"); A.Assert ((BT xor F) = Triboolean'(True), "BT xor F"); A.Assert ((BT xor T) = Triboolean'(False), "BT xor T"); A.Assert ((BT xor I) = Indeterminate, "BT xor I"); A.Assert ((T xor F) = Triboolean'(True), "T xor F"); A.Assert ((T xor T) = Triboolean'(False), "T xor T"); A.Assert ((T xor I) = Indeterminate, "T xor I"); A.Assert ((F xor F) = Triboolean'(False), "F xor F"); A.Assert ((F xor T) = Triboolean'(True), "F xor T"); A.Assert ((F xor I) = Indeterminate, "F xor I"); A.Assert ((I xor F) = Indeterminate, "I xor F"); A.Assert ((I xor T) = Indeterminate, "I xor T"); A.Assert ((I xor I) = Indeterminate, "I xor I"); ----------- -- Not -- ----------- A.Assert (not F = Triboolean'(True), "not F"); A.Assert (not T = Triboolean'(False), "not T"); A.Assert (not I = Indeterminate, "not I"); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/tribools/simple/test.yaml0000644000175000017500000000005013661715457024363 0ustar nicolasnicolasdescription: Test for GNATCOLL.Tribools gnatcoll-core-21.0.0/testsuite/tests/paragraph_filling/0000755000175000017500000000000013661715457023050 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/paragraph_filling/text/0000755000175000017500000000000013661715457024034 5ustar nicolasnicolasgnatcoll-core-21.0.0/testsuite/tests/paragraph_filling/text/in.txt0000644000175000017500000000112613661715457025203 0ustar nicolasnicolasThe game still goes on. The guns of the rovers are silenced; but the tintype man, the enlarged photograph brigand, the kodaking tourist and the scouts of the gentle brigade of fakirs have found it out, and carry on the work. The hucksters of Germany, France, and Sicily now bag its small change across their counters. Gentlemen adventurers throng the waiting-rooms of its rulers with proposals for railways and concessions. The little opera-bouffe nations play at government and intrigue until some day a big, silent gunboat glides into the offing and warns them not to break their toys. gnatcoll-core-21.0.0/testsuite/tests/paragraph_filling/text/greedy.txt0000644000175000017500000000111413661715457026051 0ustar nicolasnicolasThe game still goes on. The guns of the rovers are silenced; but the tintype man, the enlarged photograph brigand, the kodaking tourist and the scouts of the gentle brigade of fakirs have found it out, and carry on the work. The hucksters of Germany, France, and Sicily now bag its small change across their counters. Gentlemen adventurers throng the waiting-rooms of its rulers with proposals for railways and concessions. The little opera-bouffe nations play at government and intrigue until some day a big, silent gunboat glides into the offing and warns them not to break their toys. gnatcoll-core-21.0.0/testsuite/tests/paragraph_filling/text/pretty.txt0000644000175000017500000000111413661715457026121 0ustar nicolasnicolasThe game still goes on. The guns of the rovers are silenced; but the tintype man, the enlarged photograph brigand, the kodaking tourist and the scouts of the gentle brigade of fakirs have found it out, and carry on the work. The hucksters of Germany, France, and Sicily now bag its small change across their counters. Gentlemen adventurers throng the waiting-rooms of its rulers with proposals for railways and concessions. The little opera-bouffe nations play at government and intrigue until some day a big, silent gunboat glides into the offing and warns them not to break their toys. gnatcoll-core-21.0.0/testsuite/tests/paragraph_filling/text/knuth.txt0000644000175000017500000000111413661715457025723 0ustar nicolasnicolasThe game still goes on. The guns of the rovers are silenced; but the tintype man, the enlarged photograph brigand, the kodaking tourist and the scouts of the gentle brigade of fakirs have found it out, and carry on the work. The hucksters of Germany, France, and Sicily now bag its small change across their counters. Gentlemen adventurers throng the waiting-rooms of its rulers with proposals for railways and concessions. The little opera-bouffe nations play at government and intrigue until some day a big, silent gunboat glides into the offing and warns them not to break their toys. gnatcoll-core-21.0.0/testsuite/tests/paragraph_filling/text/test.adb0000644000175000017500000000546413661715457025474 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Strings.Unbounded; with GNAT.Strings; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Paragraph_Filling; use GNATCOLL.Paragraph_Filling; with Test_Assert; function Test return Integer is package A renames Test_Assert; package ASU renames Ada.Strings.Unbounded; subtype Acc is GNAT.Strings.String_Access; S_In : constant Acc := Read_File (Create_From_Base ("in.txt")); S_Gr : constant Acc := Read_File (Create_From_Base ("greedy.txt")); S_Pr : constant Acc := Read_File (Create_From_Base ("pretty.txt")); S_Kn : constant Acc := Read_File (Create_From_Base ("knuth.txt")); begin -- Test no-fill option A.Assert (S_In.all, ASU.To_String (No_Fill (S_In.all, 60)), "no fill"); -- Test greedy option A.Assert (S_Gr.all, ASU.To_String (Greedy_Fill (S_In.all, 60)), "greedy fill"); -- Test pretty option A.Assert (S_Pr.all, ASU.To_String (Pretty_Fill (S_In.all, 60)), "pretty fill"); -- Test Knuth option A.Assert (S_Kn.all, ASU.To_String (Knuth_Fill (S_In.all, 60)), "Knuth fill"); return A.Report; end Test; gnatcoll-core-21.0.0/testsuite/tests/paragraph_filling/text/test.yaml0000644000175000017500000000030413661715457025674 0ustar nicolasnicolasdescription: Simple test for GNATCOLL.Paragraph_Filling. data: # Henry, O. Cabbages and Kings. New York, Doubleday, Page & company, 1910. https://www.loc.gov/item/42026107/. - "*.txt" gnatcoll-core-21.0.0/src/0000755000175000017500000000000013743647711014771 5ustar nicolasnicolasgnatcoll-core-21.0.0/src/gnatcoll-json.ads0000644000175000017500000005313313743647711020241 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2011-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- GNATCOLL.JSON exposes an API to parse and serialize data using the JSON -- (JavaScript Object Notation) format. -- -- Parsing JSON is as easy as calling the Read function:: -- -- Data : JSON_Value := Read ("[1, ""foo"", {""foo"": null}]"); -- -- Encoding to JSON is not any more complex:: -- -- JSON_String : String := Write (Data); -- -- JSON trees (JSON_Value) are available for both inspection and -- modification:: -- -- Float_Number : JSON_Value := Create (Float'(1.0)); -- -- Mere float number -- -- Object : JSON_Value := Get (Get (Data), 3); -- -- JSON object from Data: {"foo": null} -- -- Some_Array : JSON_Value := -- Create (Float_Number & Object & Create (False)); -- -- Synthetic JSON array: [1.0, {"foo": null}, False] -- -- -- Modify Data in place -- Data.Append (Some_Array); with Ada.Finalization; with Ada.Strings.Unbounded; with GNATCOLL.Strings; private with Ada.Containers.Vectors; private with GNATCOLL.Atomic; package GNATCOLL.JSON is type JSON_Value_Type is (JSON_Null_Type, -- Null value: all such JSON values are equivalent JSON_Boolean_Type, -- Boolean value: either true or false JSON_Int_Type, -- Integer value, encoded as an Ada Long_Long_Integer JSON_Float_Type, -- Float value, encoded as an Ada Long_Float JSON_String_Type, -- UTF-8 encoded string JSON_Array_Type, -- Array of JSON values JSON_Object_Type -- Sequence of fields. Each field has a unique name and maps to a -- JSON value. Depending on the context, this sequence can be processed -- as a mapping, because each field name is unique, but iterating on -- fields is deterministic because it is a sequence underneath. ); -- Each JSON value (JSON_Value below) has a specific kind... subtype JSON_Elementary_Value_Type is JSON_Value_Type range JSON_Null_Type .. JSON_String_Type; -- Some are atoms... subtype JSON_Container_Value_Type is JSON_Value_Type range JSON_Array_Type .. JSON_Object_Type; -- While others are containers for other values Invalid_JSON_Stream : exception; subtype UTF8_String is String; type UTF8_String_Access is access all UTF8_String; subtype UTF8_Unbounded_String is Ada.Strings.Unbounded.Unbounded_String; subtype UTF8_XString is GNATCOLL.Strings.XString; type JSON_Value is tagged private; -- Store a JSON value, which can be either a simple type (integer, string, -- ...) or an object with multiple fields, or an array (see JSON_Value_Type -- above). -- -- This type has by-reference semantics, so using the standard assignment -- operator as in:: -- -- A := B; -- -- just creates an alias. This means that modifying B will also modify A -- (and modifying A will of course modify B). -- -- If you want to create a separate copy, you must use the Clone function. type JSON_Array is private with Iterable => (First => Array_First, Next => Array_Next, Has_Element => Array_Has_Element, Element => Array_Element); -- JSON array type. If an object of type JSON_Array is not otherwise -- initialized, it is initialized to Empty_Array. -- -- Note that we use the Iterable aspect instead of the standard Ada 2012 -- iterator aspects because the latter brings impossible constraints: Ada -- 2012 iterators require JSON_Array to be tagged, which would break the -- existing API: the Get function would be dispatching over more than one -- type (JSON_Array because of the Arr argument, and JSON_Value because of -- the return type). JSON_Null : constant JSON_Value; Empty_Array : constant JSON_Array; -------------------- -- Array handling -- -------------------- function Is_Empty (Arr : JSON_Array) return Boolean; -- Return whether Arr is an empty array function Length (Arr : JSON_Array) return Natural; -- Return the number of elements in Arr function Get (Arr : JSON_Array; Index : Positive) return JSON_Value; -- If Arr has at least Index elements, return the element at that index. -- Raise a Constraint_Error otherwise. procedure Append (Arr : in out JSON_Array; Val : JSON_Value); -- Append Val as a new element at the end of the Arr array procedure Prepend (Arr : in out JSON_Array; Val : JSON_Value); -- Insert Val as the first element of the Arr array procedure Clear (Arr : in out JSON_Array); -- Remove all elements in Arr procedure Sort (Arr : in out JSON_Array; Less : access function (Left, Right : JSON_Value) return Boolean); -- Reorder the elements in Arr such that they are sorted smallest first -- according to the strict comparison that Less implements. procedure Set_Element (Arr : in out JSON_Array; Index : Positive; Item : JSON_Value); -- If Arr has at least Index elements, replace the element at that index -- with Item. Raise a Constraint_Error otherwise. -- Both functions below are less efficient than Append because they -- result in an extra copy of the array, but they are easier to use when -- manipulating small arrays. function "&" (Arr : JSON_Array; Value : JSON_Value) return JSON_Array; -- Return a new array that appends Value to Arr function "&" (Value1, Value2 : JSON_Value) return JSON_Array; -- Return a new array that contains Value1 and Value2 function Is_Empty (Val : JSON_Value) return Boolean; -- Return True if Val is empty array, empty object or null value. Return -- False in all other cases. --------------------- -- Array iteration -- --------------------- function Array_First (Arr : JSON_Array) return Positive; function Array_Next (Arr : JSON_Array; Index : Positive) return Positive; function Array_Has_Element (Arr : JSON_Array; Index : Positive) return Boolean; function Array_Element (Arr : JSON_Array; Index : Positive) return JSON_Value; ---------------------------------------------- -- Serialization/deserialization primitives -- ---------------------------------------------- type Parsing_Error is record Line, Column : Positive; -- Line and column numbers at which a parsing error is detected Message : UTF8_Unbounded_String; -- Short description of the parsing error end record; function Format_Parsing_Error (Error : Parsing_Error) return String; -- Return a human-readable string to describe Error type Read_Result (Success : Boolean := True) is record case Success is when True => Value : JSON_Value; when False => Error : Parsing_Error; end case; end record; function Read (Strm : Ada.Strings.Unbounded.Unbounded_String; Filename : String := "") return JSON_Value; function Read (Strm : String; Filename : String := "") return JSON_Value; -- Parse the JSON document in Strm and return it. On parsing error, print -- an error message referencing Filename on the standard output and raise -- an Invalid_JSON_Stream exception. function Read (Strm : Ada.Strings.Unbounded.Unbounded_String) return Read_Result; function Read (Strm : String) return Read_Result; -- Parse the JSON document in Strm and return it. If there is a parsing -- error, return the corresponding error information. function Write (Item : JSON_Value; Compact : Boolean := True) return String; function Write (Item : JSON_Value; Compact : Boolean := True) return Ada.Strings.Unbounded.Unbounded_String; -- Return a string that encodes Item in JSON. Unless Compact is True, this -- creates an indented multi-line representation. ----------------------------- -- Creation of JSON values -- ----------------------------- function Create return JSON_Value with Post => Create'Result.Kind = JSON_Null_Type; -- Create a 'null' JSON value function Create (Val : Boolean) return JSON_Value with Post => Create'Result.Kind = JSON_Boolean_Type; -- Create a boolean-typed JSON value function Create (Val : Integer) return JSON_Value with Post => Create'Result.Kind = JSON_Int_Type; function Create (Val : Long_Integer) return JSON_Value with Post => Create'Result.Kind = JSON_Int_Type; function Create (Val : Long_Long_Integer) return JSON_Value with Post => Create'Result.Kind = JSON_Int_Type; -- Create an integer-typed JSON value function Create (Val : Float) return JSON_Value with Post => Create'Result.Kind = JSON_Float_Type; -- Create a float-typed JSON value function Create (Val : Long_Float) return JSON_Value with Post => Create'Result.Kind = JSON_Float_Type; function Create (Val : UTF8_String) return JSON_Value with Post => Create'Result.Kind = JSON_String_Type; -- Create a string-typed JSON value function Create (Val : UTF8_Unbounded_String) return JSON_Value with Post => Create'Result.Kind = JSON_String_Type; -- Create a string-typed JSON value function Create (Val : UTF8_XString) return JSON_Value with Post => Create'Result.Kind = JSON_String_Type; -- Create a string-typed JSON value function Create (Val : JSON_Array) return JSON_Value with Post => Create'Result.Kind = JSON_Array_Type; -- Create a JSON value from the JSON array function Create_Object return JSON_Value with Post => Create_Object'Result.Kind = JSON_Object_Type; -- Create an empty object. Values need to be added using the below -- Set_Field methods. procedure Sort (Val : in out JSON_Value; Less : access function (Left, Right : JSON_Value) return Boolean); -- If Val is a JSON array or a JSON object, reorder its elements/fields -- such that they are sorted smallest first according to the strict -- comparison that Less implements. Note that for JSON objects, field -- values are compared, not field names. procedure Append (Arr : JSON_Value; Item : JSON_Value) with Pre => Arr.Kind = JSON_Array_Type; -- Assuming Arr is a JSON array, append Item to it function Clone (Val : JSON_Value) return JSON_Value; -- Return a deep clone of Val. Any later change in Val or its fields -- (recursively) will have no impact on the resulting value. function "=" (Left, Right : JSON_Value) return Boolean; -- Return whether Left and Right are structurally identical. -- -- The actual contents is compared, not the pointers. So two objects -- constructed independently, with the same contents, will match. For JSON -- objects, the order for fields is irrelevant, for objects. It is relevant -- for arrays however. procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : JSON_Value) with Pre => Val.Kind = JSON_Object_Type; -- Assuming Val is a JSON object, add a new field or modify the existing -- one for the given Field_Name. The field value is Field afterwards. procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_XString; Field : JSON_Value) with Pre => Val.Kind = JSON_Object_Type; -- Assuming Val is a JSON object, add a new field or modify the existing -- one for the given Field_Name. The field value is Field afterwards. -- All the Set_Field overloads below are convenience shortcut that first -- create a JSON value from their Field argument and then call the above -- Set_Field procedures with the result. procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : Boolean) with Pre => Val.Kind = JSON_Object_Type; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : Integer) with Pre => Val.Kind = JSON_Object_Type; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : Long_Integer) with Pre => Val.Kind = JSON_Object_Type; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : Float) with Pre => Val.Kind = JSON_Object_Type; procedure Set_Field_Long_Float (Val : JSON_Value; Field_Name : UTF8_String; Field : Long_Float) with Pre => Val.Kind = JSON_Object_Type; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : UTF8_String) with Pre => Val.Kind = JSON_Object_Type; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : UTF8_Unbounded_String) with Pre => Val.Kind = JSON_Object_Type; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : JSON_Array) with Pre => Val.Kind = JSON_Object_Type; -- This performs a a shallow copy of Field, so any change you do to the -- passed array for Field afterwards will not impact Val. procedure Set_Field_If_Not_Empty (Val : JSON_Value; Field_Name : UTF8_String; Field : UTF8_Unbounded_String) with Pre => Val.Kind = JSON_Object_Type; -- Set Field only if it is not empty string procedure Set_Field_If_Not_Empty (Val : JSON_Value; Field_Name : UTF8_String; Field : UTF8_String) with Pre => Val.Kind = JSON_Object_Type; -- Set Field only if it is not empty string procedure Set_Field_If_Not_Empty (Val : JSON_Value; Field_Name : UTF8_String; Field : JSON_Array) with Pre => Val.Kind = JSON_Object_Type; -- Set Field only if it is not empty array. -- This performs a a shallow copy of Field, so any change you do to the -- passed array for Field afterwards will not impact Val. procedure Unset_Field (Val : JSON_Value; Field_Name : UTF8_String) with Pre => Val.Kind = JSON_Object_Type; -- Assuming Val is a JSON object, remove its field whose name matches -- Field_Name. Do nothing if there is no such a field. ------------------------------------------------------ -- Conversions from JSON values to native Ada types -- ------------------------------------------------------ function Kind (Val : JSON_Value) return JSON_Value_Type; -- Return the kind corresponding to the Val JSON value function Get (Val : JSON_Value) return Boolean with Pre => Val.Kind = JSON_Boolean_Type; function Get (Val : JSON_Value) return Integer with Pre => Val.Kind = JSON_Int_Type; function Get (Val : JSON_Value) return Long_Integer with Pre => Val.Kind = JSON_Int_Type; function Get (Val : JSON_Value) return Long_Long_Integer with Pre => Val.Kind = JSON_Int_Type; function Get (Val : JSON_Value) return Float with Pre => Val.Kind = JSON_Float_Type; function Get_Long_Float (Val : JSON_Value) return Long_Float with Pre => Val.Kind = JSON_Float_Type; function Get (Val : JSON_Value) return UTF8_String with Pre => Val.Kind = JSON_String_Type; function Get (Val : JSON_Value) return UTF8_Unbounded_String with Pre => Val.Kind = JSON_String_Type; function Get (Val : JSON_Value) return UTF8_XString with Pre => Val.Kind = JSON_String_Type; function Get (Val : JSON_Value) return JSON_Array with Pre => Val.Kind = JSON_Array_Type; function Has_Field (Val : JSON_Value; Field : UTF8_String) return Boolean with Pre => Val.Kind = JSON_Object_Type; -- Assuming Val is a JSON object, return whether it contains a field whose -- name is Field. function Get (Val : JSON_Value; Field : UTF8_String) return JSON_Value with Pre => Val.Kind = JSON_Object_Type; function Get (Val : JSON_Value; Field : UTF8_String) return Boolean with Pre => Val.Kind = JSON_Object_Type and then Get (Val, Field).Kind = JSON_Boolean_Type; function Get (Val : JSON_Value; Field : UTF8_String) return Integer with Pre => Val.Kind = JSON_Object_Type and then Get (Val, Field).Kind = JSON_Int_Type; function Get (Val : JSON_Value; Field : UTF8_String) return Long_Integer with Pre => Val.Kind = JSON_Object_Type and then Get (Val, Field).Kind = JSON_Int_Type; function Get (Val : JSON_Value; Field : UTF8_String) return Float with Pre => Val.Kind = JSON_Object_Type and then Get (Val, Field).Kind = JSON_Float_Type; function Get_Long_Float (Val : JSON_Value; Field : UTF8_String) return Long_Float with Pre => Val.Kind = JSON_Object_Type and then Get (Val, Field).Kind = JSON_Float_Type; function Get (Val : JSON_Value; Field : UTF8_String) return UTF8_String with Pre => Val.Kind = JSON_Object_Type and then Get (Val, Field).Kind = JSON_String_Type; function Get (Val : JSON_Value; Field : UTF8_String) return UTF8_Unbounded_String with Pre => Val.Kind = JSON_Object_Type and then Get (Val, Field).Kind = JSON_String_Type; function Get (Val : JSON_Value; Field : UTF8_String) return JSON_Array with Pre => Val.Kind = JSON_Object_Type and then Get (Val, Field).Kind = JSON_Array_Type; --------------- -- Iteration -- --------------- procedure Map_JSON_Object (Val : JSON_Value; CB : access procedure (Name : UTF8_String; Value : JSON_Value)) with Pre => Val.Kind = JSON_Object_Type; -- Assuming Val is a JSON object, call CB on all its fields generic type Mapped (<>) is private; procedure Gen_Map_JSON_Object (Val : JSON_Value; CB : access procedure (User_Object : in out Mapped; Name : UTF8_String; Value : JSON_Value); User_Object : in out Mapped) with Pre => Val.Kind = JSON_Object_Type; -- Assuming Val is a JSON object, call CB on all its field, passing the -- given User_Object from call to call. private type JSON_Array_Internal; type JSON_Array_Access is access all JSON_Array_Internal; type JSON_Object_Internal; type JSON_Object_Access is access all JSON_Object_Internal; type Data_Type (Kind : JSON_Value_Type := JSON_Null_Type) is record case Kind is when JSON_Null_Type => null; when JSON_Boolean_Type => Bool_Value : Boolean; when JSON_Int_Type => Int_Value : Long_Long_Integer; when JSON_Float_Type => Flt_Value : Long_Float; when JSON_String_Type => Str_Value : UTF8_XString; when JSON_Array_Type => Arr_Value : JSON_Array_Access; when JSON_Object_Type => Obj_Value : JSON_Object_Access; end case; end record; type JSON_Value is new Ada.Finalization.Controlled with record Data : Data_Type; end record; -- We cannot merge Data_Type and JSON_Value, because JSON_Value cannot -- have a discriminant with a default value. overriding procedure Adjust (Obj : in out JSON_Value); overriding procedure Finalize (Obj : in out JSON_Value); -- JSON Array definition: package Vect_Pkg is new Ada.Containers.Vectors (Index_Type => Positive, Element_Type => JSON_Value); type JSON_Array is record Vals : Vect_Pkg.Vector; end record; type JSON_Array_Internal is record Cnt : aliased GNATCOLL.Atomic.Atomic_Counter := 1; Arr : JSON_Array; end record; Empty_Array : constant JSON_Array := (Vals => Vect_Pkg.Empty_Vector); -- JSON Object definition: type Object_Item is record Key : UTF8_XString; Val : JSON_Value; end record; package Object_Items_Pkg is new Ada.Containers.Vectors (Positive, Object_Item); type JSON_Object_Internal is record Cnt : aliased GNATCOLL.Atomic.Atomic_Counter := 1; Vals : Object_Items_Pkg.Vector; end record; JSON_Null : constant JSON_Value := (Ada.Finalization.Controlled with others => <>); -- Can't call Create, because we would need to see the body of -- Initialize and Adjust. end GNATCOLL.JSON; gnatcoll-core-21.0.0/src/gnatcoll-any_types.adb0000644000175000017500000000454613661715457021270 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package body GNATCOLL.Any_Types is ---------- -- Free -- ---------- procedure Free (X : in out Any_Type) is begin case X.T is when Integer_Type | String_Type | No_Type => -- Nothing to free for these types null; when List_Type => for J in X.List'Range loop Free (X.List (J).all); Unchecked_Free (X.List (J)); end loop; when Tuple_Type => for J in X.Tuple'Range loop Free (X.Tuple (J).all); Unchecked_Free (X.Tuple (J)); end loop; end case; end Free; end GNATCOLL.Any_Types; gnatcoll-core-21.0.0/src/gnatcoll-email-utils.ads0000644000175000017500000003011713661715457021514 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package contains various utility routines related to handling of -- email messages with Ada.Calendar; with Ada.Containers.Hashed_Sets; package GNATCOLL.Email.Utils is type Region is (Addr_Header, Other_Header, Text); subtype Any_Header is Region range Addr_Header .. Other_Header; -- Used to indicate where a given character occurs: -- Addr_Header: From/To/Cc header -- Header: any other header -- Text: message body ----------- -- Dates -- ----------- type Time_Format is (Time_RFC2822, Time_Envelope); -- The time formats supported by this package. -- Time_RFC2822 is the one used in the Date: header. -- Time_Enveloppe is the one used in the From_ header function To_Time (Date : String; Format : Time_Format := Time_RFC2822) return Ada.Calendar.Time; -- Interprets the Date as a date/time, and returns it. The time is UTC. -- If Date doesn't match Format, No_Time is returned. function Format_Date (Date : Ada.Calendar.Time; Use_GMT : Boolean := False; From_Line : Boolean := False; No_TZ : Boolean := False; Show_Time : Boolean := True; Show_Seconds : Boolean := True; Show_Day : Boolean := True) return String; -- Format the date as a RFC 2822 string, e.g.: -- Fri, 09 Nov 2001 01:08:47 -0000 -- If Use_GMT is true, the time stamp is rendered in UTC, and the time zone -- is shown as "GMT". This is needed for some protocols. -- If From_Line is True, use the format of standard UNIX mailbox From_ -- lines: -- Tue Jan 24 14:48:49 2006 +0100 -- If No_TZ is true, then the date is rendered in UTC, and no time zone -- name is shown. -- If Show_Seconds is false, then seconds will not be displayed (this can -- be used to save space, but the output format is not compatible with -- RFC 2822). -- If Show_Day is false, the day of week is not displayed. The output is -- also not compatible with RFC 2822. function Format_Time (Date : Ada.Calendar.Time) return String; -- Format the time part of Date, interpreted in UTC, as a RFC2822 string: -- 01:08:47 --------------- -- Addresses -- --------------- function Hash (Addr : Email_Address) return Ada.Containers.Hash_Type; package Address_Set is new Ada.Containers.Hashed_Sets (Email_Address, Hash, "="); function Quote (Str : String) return String; -- Return a string which is a quoted version of Str: backslashes have been -- replaced by \\, and double-quotes by \". function Unquote (Str : String) return String; -- Return an unquoted version of Str. Extra Backslashes are removed function Parse_Address (Email : String) return Email_Address; -- Split an email address as read from a message header into its -- constituents function To_Address (Address : String; Real_Name : String := "") return Email_Address; -- Create an Email_Address from the given parts function Get_Addresses (Str : String) return Address_Set.Set; function Get_Addresses (Str : Charset_String_List.List) return Address_Set.Set; -- Return the list of addresses in Str. -- The second version properly preserves real names from extended charsets. function To_String (Addresses : Address_Set.Set; Separator : String := ", "; Address_Only : Boolean := False; Charset : String := Charset_US_ASCII) return String; -- Return the list of addresses as a string compatible with RFC 2822. -- Parsing this field with Get_Addresses would return the same set of -- addresses of Separator has its default value. -- If Address_Only is true, then the real names are never shown in the -- string. -- Charset is passed to Format_Address to format individual addresses. function Get_Recipients (Msg : Message'Class; Include_From : Boolean := False) return Address_Set.Set; function Get_Recipients (H : Header'Class) return Address_Set.Set; -- Return the list of all recipients of the message. This takes into -- account all occurrences of all relevant headers. -- In the first case, Include_From indicates whether the sender of the -- message should also be returned. For Null_Message, return an empty set. -- ??? The 2nd function should be renamed to Get_Addresses since it applies -- to all headers containing addresses, not only those designating -- message recipients. function Legacy_Format_Address (Real : String; Address : String) return String; -- Format the given email address and real name, using quotes and -- backslash escaping to protect any special characters occurring in Real. -- Note: Real should be a US ASCII string. function Format_Address (Email : Email_Address; Charset : String := Charset_US_ASCII) return Charset_String_List.List; -- Format an email address into a proper format for RFC2822 -- Charset specifies the character set for the real name. function Format_Address (Email : Email_Address; Charset : String := Charset_US_ASCII) return Unbounded_String; -- Same as above, and return result as an RFC 2047 encoded string function Domain_From_Address (Email : String) return String; function Domain_From_Address (Email : Email_Address) return String; -- Return the domain name for the given Email address. In the first case, -- Email must only contain the address, not the real name. -- If no domain is specified, the empty string is returned. Look at -- GNAT.Socket.Host_Name to fall back on the current host. function Login_From_Address (Email : String) return String; function Login_From_Address (Email : Email_Address) return String; -- Return the login name from the given email address, i.e. the part before -- the '@'. In the first case, Email must only contain the address, not the -- real name. ---------- -- Mime -- ---------- function Get_Main_Type (MIME_Type : String) return String; -- Return the main type component of the MIME_Type, for instance "text" -- when the type is "text/plain"; function Get_Sub_Type (MIME_Type : String) return String; -- Return the sub type component of the MIME_Type, for instance "plain" -- when the type is "text/plain"; --------------- -- Encodings -- --------------- procedure Quoted_Printable_Encode (Str : String; Charset : String; Max_Block_Len : Integer := Integer'Last; Where : Region := Text; Result : out Unbounded_String); -- Encode Str in quoted-printable format, as per RFC 2045/2047. -- This should be used for ASCII-like charsets, like all ISO-8859-* -- charsets, i.e. when most of the characters are already in the ASCII -- charset (0 through 127). procedure Quoted_Printable_Decode (Str : String; Result : out Unbounded_String; Where : Region := Text); -- Decode Str as a quoted-printable encoded string as per RFC 2045. -- The returned value may contain non - ASCII characters, their -- interpretation is left to the called (i.e. the charset is unknown). -- If the optional argument header is present and true, underscore will be -- decoded as space. This is used to decode "Q" encoded headers as -- described in RFC 2047: "MIME (Multipurpose Internet Mail Extensions) -- Part Three: Message Header Extensions for Non-ASCII Text". procedure Base64_Encode (Str : String; Charset : String; Max_Block_Len : Integer := Integer'Last; Where : Region := Text; Result : out Unbounded_String); -- Encode Str in base64 format, as defined by RFC 2045. -- This should be used for charsets that have little similarity with -- ASCII, for instance Asian charsets. procedure Base64_Encode (Str : String; Charset : String; Max_Block_Len : Integer := Integer'Last; Where : Region := Text; Put_Parts : not null access procedure (Part : String)); -- Encode Str in base64 format like above but returning result part by part -- over Put_Parts routine to be able to use another kind of output. procedure Base64_Decode (Str : String; Result : out Unbounded_String); -- Decode Str from a base64 encoding, as defined by RFC 2045 procedure Encode (Str : String; Charset : String := Charset_US_ASCII; Where : Region := Text; Result : out Unbounded_String); -- Encode Str in the best encoding to use for Charset. The encoding depends -- on how close charset is to ASCII. -- If Header is true, then several encoded blocks will be created as -- required by RFC 2045 (separated by spaces). In addition, the charset is -- included as part of the encoded field, as suitable for mail headers. procedure Decode_Header (Str : String; Default_Charset : String := Charset_US_ASCII; Result : out Charset_String_List.List; Where : Any_Header := Other_Header); -- Decode Str. It might contain several mime-encoded sections, with -- different charsets. Each section is returned separately. For each -- section, Contents must be interpreted in the context of that charset. -- When several adjacent sections have the same encoding, they are merged -- for ease of use. -- When no charset is specified for a section, the default charset is -- assumed. -- This function can also be used for headers, when each section starts -- with =?charset?q?....?=. -------------- -- Charsets -- -------------- procedure Flatten (List : Charset_String_List.List; Result : out Unbounded_String); -- Return a flatten version of List, where all sections are concatenated. -- It will not be possible to go back to List afterward, since the sections -- are not MIME-encoded, only their contents is taken into account. -- This should never be used for display to the user, only for internal -- manipulation when the exact charset of each section is irrelevant. procedure To_String (List : Charset_String_List.List; Result : out Unbounded_String; Where : Any_Header := Other_Header); -- Return a single string representing list, where all sections is -- properly encoded and surrounded by =?charset? markers. end GNATCOLL.Email.Utils; gnatcoll-core-21.0.0/src/executable_path.c0000644000175000017500000000521513661715457020277 0ustar nicolasnicolas/*---------------------------------------------------------------------------- -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -----------------------------------------------------------------------------*/ /* The function is the low level part of GNATCOLL.Utils.Executable_Path. */ #if defined (__APPLE__) /* MacOS implementation */ #include #include #include int c_executable_path (char *buffer, int size) { int status; uint32_t bufsize = (uint32_t) size; status = _NSGetExecutablePath(buffer, &bufsize); if (status == 0) { return strlen(buffer); } else { return 0; } } #elif defined (__MINGW32__) || defined (__CYGWIN__) /* Windows Implementation */ #include int c_executable_path (char *buffer, int size) { return (int) GetModuleFileNameA(NULL, buffer, (DWORD) size); } #elif defined (__linux__) /* Linux implementation */ #include int c_executable_path (char *buffer, int size) { return readlink("/proc/self/exe", buffer, (size_t) size); } #else /* Dummy implementation */ int c_executable_path (char *buffer, int size) { return 0; } #endif gnatcoll-core-21.0.0/src/gnatcoll-vfs.ads0000644000175000017500000006667313743647711020103 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package abstracts file operations and names. -- It is a layer on top of GNATCOLL.Filesystem, which allows you to use the -- same code to manipulate (copy, rename, delete,...) files, independent of -- the actual system you are running on (or even if the files happen to be on -- a remote host). -- This package provides additional abstraction with regards to file names. -- Depending on the context, your application will sometime need to use base -- names (no directory), or full name to reference a file. It is not always -- clear from the API which type of name is expected, and this package allows -- you to pass a Virtual_File instead, from which you can extract either the -- base name or the full name, as needed. This package also abstracts whether -- file names are case-sensitive or not (in fact, all systems can be -- considered as case sensitive because file names should be displayed with -- the exact casing that the user has chosen -- but in some cases the files -- can be referenced through multiple casing). -- It also takes care of reference counting, and will therefore free memory as -- appropriate when it is no longer needed. That makes the type relatively -- light weight, all the more because most of the information is computed only -- when needed, and cached in some cases. -- There is however a cost associated with Virtual_File: they are controlled -- types, and as such generate a lot of extra code; and they require at least -- one memory allocation when the file is created to store the name. with Ada.Calendar; with Ada.Containers; with Ada.Finalization; with Ada.Strings.Unbounded; with Ada.Unchecked_Deallocation; with Interfaces.C.Strings; use Interfaces.C.Strings; with GNAT.OS_Lib; with GNAT.Strings; private with GNATCOLL.IO; private with GNATCOLL.IO.Native; with GNATCOLL.Strings; package GNATCOLL.VFS is ------------------------ -- Filesystem strings -- ------------------------ type Filesystem_String is new String; type Filesystem_String_Access is access all Filesystem_String; -- A Filesystem_String represents an array of characters as they are -- represented on the filesystem, without any encoding consideration. function "+" (S : Filesystem_String) return String; pragma Inline ("+"); function "+" (S : String) return Filesystem_String; pragma Inline ("+"); function Equal (S1, S2 : Filesystem_String) return Boolean; pragma Inline (Equal); procedure Free is new Ada.Unchecked_Deallocation (Filesystem_String, Filesystem_String_Access); -- Conversion/Comparison/Concatenation functions type Cst_Filesystem_String_Access is access constant Filesystem_String; ---------------- -- Exceptions -- ---------------- VFS_Directory_Error : exception; VFS_Invalid_File_Error : exception; VFS_Remote_Config_Error : exception; ------------------------------ -- Virtual File definition -- ------------------------------ type Virtual_File is tagged private; No_File : aliased constant Virtual_File; -- Note: a default initialized Virtual_File object has the value No_File --------------- -- Constants -- --------------- Local_Host : aliased constant String; ------------------- -- Configuration -- ------------------- procedure Symbolic_Links_Support (Active : Boolean); -- Whether this package should do extra system calls to handle symbolic -- links. -- This is automatically False on platforms like Windows where this notion -- does not exist, but when you know you have no symbolic links manipulated -- by your application you can significantly reduce the number of system -- calls (which in turns speeds things up). If you set it to False, two -- symbolic links that point to the same physical file will be considered -- different by the "=" operator. If you set it to True they will be -- considered equal. -- Changing this is not thread safe. In fact, you should call this before -- manipulating any of the Virtual_File, because GNATCOLL.VFS caches the -- normalization of file names, and would not redo it for existing files -- after you call this function, so the results of "=" in particular might -- be unexpected. ---------------------------- -- Creating Virtual_File -- ---------------------------- -- The following subprograms are used to create instances of Virtual_File. -- On the disk, a filename is typically just a series of bytes, with no -- special interpretation in utf8, iso-8859-1 or other pagesets (on most -- systems, windows always uses utf8 these days but has other -- specificities). -- As a result, a filename passed to these Create subprograms will not be -- interpreted through an encoding or another, but will just be stored as -- is. However, when comes the time to display the file on the disk, the -- filename needs to be converted to a known encoding, generally utf8. -- See the "Retrieving names" section below. function Create (Full_Filename : Filesystem_String; Host : String := Local_Host; Normalize : Boolean := False) return Virtual_File; -- Return a file, given its full filename. -- The latter can be found, for source files, through the functions in -- projects-registry.ads. -- If Normalize is set, then the VFS is created using the normalized -- Full_Filename. In that case note that the path passed is transformed -- into an absolute path. function Create_From_Dir (Dir : Virtual_File; Base_Name : Filesystem_String; Normalize : Boolean := False) return Virtual_File; -- Creates a file from its directory and base name -- If Normalize is set, then Create_From_Dir will make sure that the -- path is normalized function Create_From_Base (Base_Name : Filesystem_String; Base_Dir : Filesystem_String := ""; Host : String := Local_Host) return Virtual_File; -- Create a file from its base name. -- if Base_Name is an absolute path, then the file is created as is -- else the file is created relative to Base_Dir or the Current Directory -- if provided. function Create_From_UTF8 (Full_Filename : String; Host : String := Local_Host; Normalize : Boolean := False) return Virtual_File; -- Creates a file from its display name -- If Normalize is set, then the VFS is created using the normalized -- Full_Filename. function Locate_On_Path (Base_Name : Filesystem_String; Host : String := Local_Host) return Virtual_File; -- Locate the file from its base name and the PATH environment variable function Join (Self : Virtual_File; File : Virtual_File) return Virtual_File; function Join (Self : Virtual_File; Path : Filesystem_String) return Virtual_File; function "/" (Self : Virtual_File; File : Virtual_File) return Virtual_File; function "/" (Self : Virtual_File; Path : Filesystem_String) return Virtual_File; function "/" (Dir : Filesystem_String; File : Virtual_File) return Virtual_File; pragma Inline (Join, "/"); -- Various ways to build paths from their elements. These are just -- convention functions on top of the Create_* functions, but might help -- make the code more concise. For instance: -- File : constant Virtual_File := -- Get_Current_Dir / "filename.txt"; ---------------------- -- Retrieving names -- ---------------------- -- As mentioned above, a filename is stored internally as a series of bytes -- and not interpreted in anyway for an encoding. However, when you -- retrieve the name of a file for display, you will have to convert it to -- a known encoding. -- There are two sets of functions for retrieving names: Display_* will -- return the name converted through the Locale_To_Display function of the -- filesystem. -- All other functions will return the name as passed to the Create -- functions above, and therefore make no guarantee on the encoding of the -- file name. function Base_Name (File : Virtual_File; Suffix : Filesystem_String := ""; Normalize : Boolean := False) return Filesystem_String; -- Return the base name of the file function Base_Dir_Name (File : Virtual_File) return Filesystem_String; -- Return the base name of the directory or the file function Full_Name (File : Virtual_File; Normalize : Boolean := False; Resolve_Links : Boolean := False) return Cst_Filesystem_String_Access; -- Return the full path to File. -- If Normalize is True, the file name is first normalized, note that links -- are not resolved there by default, unless you specify Resolve_Links to -- True. -- The returned value can be used to recreate a Virtual_File instance. -- If file names are case insensitive, the normalized name will always -- be all lower cases. function Full_Name (File : Virtual_File; Normalize : Boolean := False) return Filesystem_String; -- Same as above, returning a filesystem_string function Full_Name_Hash (Key : Virtual_File) return Ada.Containers.Hash_Type; -- Return a Hash_Type computed from the full name of the given VFS. -- Could be used to instantiate an Ada 2005 container that uses a VFS as -- key and requires a hash function. -- See File_Sets below. function File_Extension (File : Virtual_File; Normalize : Boolean := False) return Filesystem_String; -- Return the extension of the file, or the empty string if there is no -- extension. This extension includes the last dot and all the following -- characters. -- If Normalize is true, the casing is normalized (depending on whether the -- platform uses case insensitive file names). function Dir_Name (File : Virtual_File) return Filesystem_String; -- Return the directory name for File. This includes any available -- on the protocol, so that relative files names are properly found. function Display_Full_Name (File : Virtual_File; Normalize : Boolean := False) return String; -- Same as Full_Name function Display_Base_Name (File : Virtual_File; Suffix : Filesystem_String := "") return String; -- Same as Base_Name function Display_Dir_Name (File : Virtual_File) return String; -- Same as Dir_Name function Display_Base_Dir_Name (File : Virtual_File) return String; -- Same as Base_Dir_Name function Unix_Style_Full_Name (File : Virtual_File; Cygwin_Style : Boolean := False; Normalize : Boolean := False; Casing : Boolean := False) return Filesystem_String; -- Returns the file path using a unix-style path. -- The casing of the filename is not impacted unless Casing is True -- (i.e. we do not convert to lower-cases on case-insensitive systems), -- because applications should preserve the original casing as much as -- possible. function Relative_Path (File : Virtual_File; From : Virtual_File) return Filesystem_String; -- Return the path of File relative to From. Return the full_name in case -- From and File are not on the same drive. function Has_Suffix (File : Virtual_File; Suffix : Filesystem_String) return Boolean; -- Tell if File has suffix Suffix function To_Remote (File : Virtual_File; To_Host : String) return Virtual_File; -- Convert the file format of File to the convention used on To_Host, -- using all available mount points defined for To_Host. function To_Local (File : Virtual_File) return Virtual_File; -- Convert the file format of File to the local filesystem's convention, -- potentially using mount points defined between File's host and local -- host. function To_Arg (File : Virtual_File; Host : String := Local_Host) return GNAT.Strings.String_Access; -- Convert the File to a String Access that can be used as argument for -- spawning a process on "Host". The returned value needs to be freed by -- the caller. ------------------------ -- Getting attributes -- ------------------------ function Is_Local (File : Virtual_File) return Boolean; -- Whether File is local to the host or is a remote file function Get_Host (File : Virtual_File) return String; -- Retrieve the host of the file, or Local_Host if the file is local to the -- host. function Is_Regular_File (File : Virtual_File) return Boolean; -- Whether File corresponds to an actual file on the disk. -- This also works for remote files. function Size (File : Virtual_File) return Long_Integer; -- The size of the file overriding function "=" (File1, File2 : Virtual_File) return Boolean; -- Overloading of the standard operator function "<" (File1, File2 : Virtual_File) return Boolean; -- Compare two files, possibly case insensitively on file systems that -- require this. function Is_Parent (Parent, Child : Virtual_File) return Boolean; -- Compare Parent and Child directory and determines if Parent contains -- Child directory function Is_Readable (File : Virtual_File) return Boolean; -- Return True if File is readable by the current process function Is_Writable (File : Virtual_File) return Boolean; -- Return True if File is writable by the current process function Is_Directory (VF : Virtual_File) return Boolean; -- Return True if File is in fact a directory function Is_Symbolic_Link (File : Virtual_File) return Boolean; -- Return True if File is a symbolic link function Is_Absolute_Path (File : Virtual_File) return Boolean; -- Return True if File contains an absolute path name, False if it only -- contains the base name or a relative name. procedure Set_Writable (File : VFS.Virtual_File; Writable : Boolean); -- If Writable is True, grant write permissions to file's owner, -- otherwise revoke write permissions. procedure Set_Readable (File : VFS.Virtual_File; Readable : Boolean); -- If Readable is True, grant read permissions to file's owner, -- otherwise revoke read permissions. -- Note that this is not supported on remote Windows. function File_Time_Stamp (File : Virtual_File) return Ada.Calendar.Time; -- Return the timestamp for this file. This is GMT time, not local time. -- Note: we do not return GNAT.OS_Lib.OS_Time, since the latter cannot be -- created by anyone, and is just a private type. -- If the file doesn't exist, No_Time is returned. procedure Normalize_Path (File : Virtual_File; Resolve_Symlinks : Boolean := False); -- Resolve '..' and '.' directories in path. -- If Resolve_Symlinks is set, then also resolve the symbolic links in -- path. -------------------- -- Array of files -- -------------------- type File_Array is array (Positive range <>) of aliased Virtual_File; type File_Array_Access is access all File_Array; procedure Unchecked_Free (Arr : in out File_Array_Access); Empty_File_Array : constant File_Array; procedure Sort (Files : in out File_Array); -- Sort the array of files, in the order given by the full names procedure Append (Files : in out File_Array_Access; F : Virtual_File); procedure Append (Files : in out File_Array_Access; F : File_Array); procedure Prepend (Files : in out File_Array_Access; F : File_Array); -- Appends one or more files to Files. Files can be null, in which case a -- new File_Array is created. procedure Remove (Files : in out File_Array_Access; F : Virtual_File); -- Remove F from Files function To_Path (Paths : File_Array) return Filesystem_String; -- Translates a list of Paths into a path string (e.g. the same format as -- $PATH) function From_Path (Path : Filesystem_String) return File_Array; -- Translate a PATH string into a list of Virtual_File function Locate_On_Path (Base_Name : Filesystem_String; Path : File_Array) return Virtual_File; -- Locate the file from its base name and the furnished list of -- directories. function Greatest_Common_Path (L : GNATCOLL.VFS.File_Array) return Virtual_File; -- Return the greatest common path to a list of files or directories -- No_File is returned if some files do not have the same root directory. function Locate_Regular_File (File_Name : Filesystem_String; Path : File_Array) return Virtual_File; -- Locate a regular file from its base name and a list of paths ------------------------- -- Manipulating files -- ------------------------- procedure Rename (File : Virtual_File; Full_Name : Virtual_File; Success : out Boolean); -- Rename a file or directory. This does not work for remote files procedure Copy (File : Virtual_File; Target_Name : Filesystem_String; Success : out Boolean); -- Copy a file or directory. This does not work for remote files procedure Delete (File : Virtual_File; Success : out Boolean); -- Remove file from the disk. This also works for remote files function Read_File (File : Virtual_File) return GNAT.Strings.String_Access; function Read_File (File : Virtual_File) return GNATCOLL.Strings.XString; -- Return the contents of an entire file, encoded with the locale encoding. -- If the file cannot be found, return null. -- The caller is responsible for freeing the returned memory. -- This works transparently for remote files. -- The second version returning a XString is in general more efficient, -- especially if you need to do operations like Split() on the resulting -- string. -------------------------- -- Directory operations -- -------------------------- Local_Root_Dir : constant Virtual_File; function Dir (File : Virtual_File) return Virtual_File; -- Return the virtual file corresponding to the directory of the file -- If File denotes a directory, then it is returned. -- To retrieve the container of File (e.g. get the parent of File, even if -- it is a directory), use Get_Parent instead. function Get_Current_Dir (Host : String := Local_Host) return Virtual_File; -- Current dir on host function Get_Tmp_Directory (Host : String := Local_Host) return Virtual_File; -- Tmp dir on host function Get_Home_Directory (Host : String := Local_Host) return Virtual_File; -- Home dir on host function Get_Logical_Drives (Host : String := Local_Host) return File_Array_Access; -- List of all logical drives on host, or null if none. The list needs to -- be freed by the caller. procedure Ensure_Directory (Dir : Virtual_File); -- Ensures that the file is a directory: add directory separator if -- needed. function Get_Root (File : Virtual_File) return Virtual_File; -- Return root directory of the file function Get_Parent (Dir : Virtual_File) return Virtual_File; -- Return the parent directory if it exists, else No_File is returned function Sub_Dir (Dir : Virtual_File; Name : Filesystem_String) return Virtual_File; -- Return sub directory Name if it exists, else No_File is returned procedure Change_Dir (Dir : Virtual_File); -- Changes working directory. Raises Directory_Error if Dir_Name does not -- exist or is not a readable directory procedure Make_Dir (Dir : Virtual_File; Recursive : Boolean := True); -- Create a new directory named Dir_Name. Raises Directory_Error if -- Dir_Name cannot be created. -- If Recursive, create all intermediary directories needed. type Read_Dir_Filter is (All_Files, Dirs_Only, Files_Only); function Read_Dir (Dir : Virtual_File; Filter : Read_Dir_Filter := All_Files) return File_Array_Access; -- Reads all entries from the directory and returns a File_Array containing -- those entries, according to filter. The list of files returned -- includes directories in systems providing a hierarchical directory -- structure, including . (the current directory) and .. (the parent -- directory) in systems providing these entries. Note that entries -- are not sorted. -- The result must be freed by the caller. function Read_Dir_Recursive (Dir : Virtual_File; Extension : Filesystem_String := ""; Filter : Read_Dir_Filter := All_Files) return File_Array_Access; -- Reads all entries from the directory, recursively, and returns all -- files with the given extension (if specified) that match the filter. -- The entries "." and ".." are never returned. Note that entries -- are not sorted. -- The result must be freed by the caller. procedure Remove_Dir (Dir : Virtual_File; Recursive : Boolean := False; Success : out Boolean); -- Delete the directory Dir. If recursive is True, this also removes all -- files or subdirectories contained in it. function Read_Files_From_Dirs (Dirs : File_Array) return File_Array_Access; -- Read all files from the list of directories Dirs type Virtual_Dir is private; Invalid_Dir : constant Virtual_Dir; function Open_Dir (Dir : Virtual_File) return Virtual_Dir; -- Opens for reading a file procedure Read (VDir : in out Virtual_Dir; File : out Virtual_File); -- Returns next file or No_File is no file is left for current directory procedure Close (VDir : in out Virtual_Dir); -- Closes the Virtual_Dir ------------------- -- Writing files -- ------------------- -- Writing is more complex than reading, since generally the whole buffer -- to write down is not available immediately, but the user wants to be -- able to write characters in a series of calls. -- The interface in this package will also support remote files. In this -- case, writing the small chunks is done in a temporary file, which is -- sent to the remote host only when the file is closed. type Writable_File is private; Invalid_File : constant Writable_File; -- Used when a file couldn't be open function "=" (Left : Writable_File; Right : Writable_File) return Boolean; -- Return True when points to the same file. function Write_File (File : Virtual_File; Append : Boolean := False) return Writable_File; -- Open File for writing. The returned handler can be used for writing. -- You must close it, otherwise the file will not actually be written in -- some cases. If Append is True then writing will be done at the end of -- the file if the file exists otherwise the file is created. -- Return Invalid_File is the file couldn't be open for writing -- -- For safety, the actual writes will occur in a temporary file unless -- Append is true, which will be renamed when calling Close. This ensures -- that the original file (if there was one) is not destroyed if for some -- reason the write fails. function Error_String (Self : Writable_File) return Ada.Strings.Unbounded.Unbounded_String; -- Return error message for last operation on file. procedure Write (File : in out Writable_File; Str : String); procedure Write (File : in out Writable_File; Str : chars_ptr); -- Write a string to File. The contents of Str are written as-is procedure Close (File : in out Writable_File); -- Closes File, and write the file to disk. -- Use_Error is raised if the file could not be saved. ---------------------------------- -- Some internally used methods -- ---------------------------------- function Convert (File : Virtual_File; To_Host : String) return Virtual_File; function Convert (File : Virtual_File; From_Dir : Virtual_File; To_Dir : Virtual_File) return Virtual_File; -- Used in mount path conversions. These should be private, but can't -- as of RM 3.9.3(10) private -- This type is implemented as a controlled type, to ease the memory -- management (so that we can have gtk+ callbacks that take a Virtual -- File in argument, without caring who has to free the memory). -- Other solutions (using Name_Id to store the strings for instance) do -- not work properly, since the functions above cannot modify File -- itself, although they do compute some information lazily). type Virtual_File is new Ada.Finalization.Controlled with record Value : GNATCOLL.IO.File_Access; end record; pragma Finalize_Storage_Only (Virtual_File); overriding procedure Finalize (File : in out Virtual_File); overriding procedure Adjust (File : in out Virtual_File); type Writable_File is record File : Virtual_File; Tmp_File : Virtual_File; FD : GNAT.OS_Lib.File_Descriptor := GNAT.OS_Lib.Invalid_FD; Append : Boolean := False; Success : Boolean := False; Error : Ada.Strings.Unbounded.Unbounded_String; end record; Invalid_File : constant Writable_File := (File => (Ada.Finalization.Controlled with Value => null), Tmp_File => (Ada.Finalization.Controlled with Value => null), FD => GNAT.OS_Lib.Invalid_FD, Append => False, Success => False, Error => <>); type Virtual_Dir is record File : Virtual_File; Files_List : File_Array_Access; Current : Natural; end record; No_File : aliased constant Virtual_File := (Ada.Finalization.Controlled with Value => null); Local_Host : aliased constant String := ""; Local_Root_Dir : constant Virtual_File := (Ada.Finalization.Controlled with Value => GNATCOLL.IO.Native.Local_Root_Dir); Empty_File_Array : constant File_Array := File_Array'(1 .. 0 => No_File); Invalid_Dir : constant Virtual_Dir := ((Ada.Finalization.Controlled with Value => null), null, 0); end GNATCOLL.VFS; gnatcoll-core-21.0.0/src/gnatcoll-symbols.ads0000644000175000017500000001115013661715457020753 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- A symbol table -- Equal strings are always represented with the same pointer, thus -- reducing the amount of memory to store multiple instances of the same -- string, and speeding up comparison (since you only need to compare the -- pointer, not the string itself). with Ada.Containers.Hashed_Sets; with GNATCOLL.Utils; use GNATCOLL.Utils; package GNATCOLL.Symbols is type Symbol_Table_Record (<>) is tagged private; type Symbol_Table_Access is access all Symbol_Table_Record'Class; -- A symbol table associating integers with strings. -- By default, this is not task safe, so you will need to extend this if -- the symbol is to be shared between multiple tasks. type Symbol is private; No_Symbol : constant Symbol; Empty_String : constant Symbol; function Allocate return Symbol_Table_Access; -- Allocate a new symbol table function Find (Table : access Symbol_Table_Record; Str : String) return Symbol; -- Return the internal version of Str. -- Comparing Symbol is the same as comparing the string itself, but much -- faster. function Get (Sym : Symbol; Empty_If_Null : Boolean := True) return Cst_String_Access; pragma Inline_Always (Get); -- The string associated with the symbol. -- The returned string must not be deallocated, it points to internal data. -- For No_Symbol, this returns null or the empty string, depending on -- Empty_If_Null. procedure Free (Table : in out Symbol_Table_Record); procedure Free (Table : in out Symbol_Table_Access); -- Free the table function Hash (S : Symbol) return Ada.Containers.Hash_Type; -- Returns a hash for the symbol, in case you need to create your own -- hash tables. function Debug_Print (S : Symbol) return String; -- Return a displaying version of symbol (debugging purposes only) procedure Display_Stats (Self : access Symbol_Table_Record); -- Display statistics about the table. -- This is meant for debug purposes only, and the output might change from -- one version to the next. private type Symbol is new Cst_String_Access; Cst_Empty_String : aliased constant String := ""; No_Symbol : constant Symbol := null; Empty_String : constant Symbol := Cst_Empty_String'Access; function Hash (Str : Cst_String_Access) return Ada.Containers.Hash_Type; function Key_Equal (Key1, Key2 : Cst_String_Access) return Boolean; pragma Inline (Hash, Key_Equal); package String_Htable is new Ada.Containers.Hashed_Sets (Element_Type => Cst_String_Access, Hash => Hash, Equivalent_Elements => Key_Equal, "=" => "="); type Symbol_Table_Record is tagged record Hash : String_Htable.Set; Calls_To_Find : Natural := 0; Total_Size : Long_Long_Integer := 0; Size_Saved : Long_Long_Integer := 0; end record; end GNATCOLL.Symbols; gnatcoll-core-21.0.0/src/gnatcoll-email.ads0000644000175000017500000007747313661715457020376 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package and its children provide routines to manipulate mailboxes and -- email messages with Ada.Calendar; with Ada.Containers.Doubly_Linked_Lists; with Ada.Finalization; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with GNATCOLL.VFS; package GNATCOLL.Email is ---------------------- -- Charset sections -- ---------------------- type Charset_String is record Contents : Unbounded_String; Charset : Unbounded_String; end record; Null_Charset_String : constant Charset_String; -- This type represents a string and its charset. Contents must be -- interpreted relatively to Charset, i.e. characters above 127 must be -- read from that charset. For instance character 161 is an inverted -- exclamation mark in iso-8859-1, but a latin letter A with ogonek in -- iso-8859-2. package Charset_String_List is new Ada.Containers.Doubly_Linked_Lists (Charset_String); -- Single-byte charsets Charset_US_ASCII : constant String := "us-ascii"; Charset_ISO_8859_1 : constant String := "iso-8859-1"; Charset_ISO_8859_2 : constant String := "iso-8859-2"; Charset_ISO_8859_3 : constant String := "iso-8859-3"; Charset_ISO_8859_4 : constant String := "iso-8859-4"; Charset_ISO_8859_9 : constant String := "iso-8859-9"; Charset_ISO_8859_10 : constant String := "iso-8859-10"; Charset_ISO_8859_13 : constant String := "iso-8859-13"; Charset_ISO_8859_14 : constant String := "iso-8859-14"; Charset_ISO_8859_15 : constant String := "iso-8859-15"; Charset_Windows_1252 : constant String := "windows-1252"; -- Multi-byte charsets Charset_UTF_8 : constant String := "utf-8"; Charset_Shift_JIS : constant String := "shift-jis"; Charset_EUC : constant String := "x-euc"; --------------- -- Addresses -- --------------- type Email_Address is record Real_Name : Unbounded_String; Address : Unbounded_String; end record; Null_Address : constant Email_Address; function "=" (Addr1, Addr2 : Email_Address) return Boolean; -- Whether Addr1 and Addr2 have the same address, even if real name differs ------------- -- Headers -- ------------- type Header is tagged private; Null_Header : constant Header; Default_Max_Header_Line_Length : constant := 76; -- Default maximal length that headers should use Content_Description : constant String := "Content-Description"; Content_Disposition : constant String := "Content-Disposition"; Content_Transfer_Encoding : constant String := "Content-Transfer-Encoding"; Content_Type : constant String := "Content-Type"; MIME_Version : constant String := "MIME-Version"; Message_ID : constant String := "Message-ID"; CC : constant String := "CC"; -- The standard MIME headers for mail messages. -- For Content_Disposition, see RFC 2183 at -- http://www.faqs.org/rfcs/rfc2183.html Text_Plain : constant String := "text/plain"; Text_Html : constant String := "text/html"; Application_Octet_Stream : constant String := "application/octet-stream"; Application_Json : constant String := "application/json"; Message_RFC822 : constant String := "message/rfc822"; Multipart_Mixed : constant String := "multipart/mixed"; Multipart_Alternative : constant String := "multipart/alternative"; Multipart_Signed : constant String := "multipart/signed"; Multipart_Digest : constant String := "multipart/digest"; Image_Jpeg : constant String := "image/jpeg"; Image_Gif : constant String := "image/gif"; Text_Xvcard : constant String := "text/x-vcard"; -- Some of the standard MIME types function Create (Name : String; Value : String; Charset : String := Charset_US_ASCII) return Header; function Create (Name : String; Value : Charset_String_List.List) return Header; -- Create a new header, with an unparsed string Value. The interpretation -- of Value depends on the specific header (it could be a date, some -- content type,...). -- Charset indicates the charset used for Value. If Value already contains -- a Mime-encoded string (such as '=?iso-8859-1?q?p=F4stal?='), the -- charset should be left to us-ascii. If Value contains extended -- characters from another charset, the latter must be specified. For -- instance, you could replace the previous mime-encoded string with: -- Value='pôstal' Charset='iso-8859-1' -- The charset influences how the header is encoded when it is displayed in -- a message. -- The Value, if it was split into several lines, must have been normalized -- and the newline characters removed. procedure Append (H : in out Header'Class; Value : String; Charset : String := Charset_US_ASCII); procedure Append (H : in out Header'Class; Value : Charset_String_List.List); -- Appends some content to the header's value procedure Set_Param (H : in out Header'Class; Param_Name : String; Param_Value : String); -- Set the value for one of H's parameters. Such parameters are typically -- used for the Content-Type header, to store the file name, or the -- boundary for instance. They appear as: -- Content-Type: text/plain; charset="iso-8859-1" -- If such a parameter is already set, it is replaced in-place, i.e. the -- order of parameters is preserved. function Get_Param (H : Header'Class; Param_Name : String) return String; -- Get the value for one of H's parameters, or "" if there is no such -- param. -- This automatically handles continuation headers, i.e. cases where the -- value of the parameter was split onto several lines, as in: -- filename*0="value1"; -- filename*1="value2" procedure Delete_Param (H : in out Header'Class; Param_Name : String); -- Remove in place one of H's parameters. -- No error is the parameter doesn't exist function Get_Name (H : Header'Class) return String; -- Return the name of the header, lower cased function Get_Value (H : Header'Class) return Charset_String_List.List; -- Return the value of the header procedure To_String (H : Header'Class; Max_Line_Len : Positive := Default_Max_Header_Line_Length; Show_Header_Name : Boolean := True; Result : out Unbounded_String); function To_String (H : Header'Class; Max_Line_Len : Positive := Default_Max_Header_Line_Length; Show_Header_Name : Boolean := True) return String; -- Return the header's value as string. Optionally, the header's name can -- be prepended. -- Lines will be split as needed to match Max_Line_Len. The first line will -- be shorted to take into account the header's name. -- The header is MIME encoded if necessary so that it only contains ASCII -- characters suitable for sending in an email message. function To_Time (H : Header'Class) return Ada.Calendar.Time; -- Interprets the header's value as a time, and returns it. This mostly -- applies to the 'Date:' header. The returned time is UTC. -- The format of the header must match the date format described in -- RFC 2822. When the format is incorrect, No_Time is returned. -------------- -- Messages -- -------------- type Message is tagged private; Null_Message : constant Message; function New_Message (MIME_Type : String := Text_Plain; Charset : String := Charset_US_ASCII) return Message; -- Return a new empty message. The memory will be freed automatically when -- the message is no longer used. -- The MIME type is the initial type, but it can be changed at any time by -- changing the header. The mail will be created as multi-part if -- MIME_Type is one of the standard multipart/* types. Otherwise, a single -- part message is created, but that will change automatically depending on -- the payload you set for the message. If MIME_Type is the empty string, -- no Content-Type header is set. function Clone_Message (Msg : Message) return Message; -- Return a copy of the given message. -- ??? In the case of a multipart message, the contents of each -- part of the message is not duplicated. In other words, modifying -- the contents of any part of the payload will affect both the -- copy and the original. function Reply_To (Msg : Message'Class; From_Email : String; From_Real_Name : String := ""; Quote : Boolean := True; Reply_All : Boolean := True; Reply_Filter : access function (Recipient : Email_Address) return Boolean := null; Local_Date : Ada.Calendar.Time := Ada.Calendar.Clock; Charset : String := Charset_US_ASCII) return Message; -- Create a new message as a reply to Msg. This impacts subjects, -- recipients,... If Quote is True, then Msg is quoted in the payload of -- the new message. -- Headers are set so that the reply will appear in the same thread as Msg -- in mailers that support threads. Charset, is supplied, is used for -- encoding of From_Real_Name. If Reply_All is True, all recipients of -- the original message are added to the Cc: header of the reply. If -- in addition Reply_Filter is not null, then only recipients for which -- Reply_Filter returns True are added. procedure Set_Default_Headers (Msg : in out Message'Class; From_Email : String; Subject : String := "No Subject"; From_Real_Name : String := ""; Local_Date : Ada.Calendar.Time := Ada.Calendar.Clock; Charset : String := Charset_US_ASCII); -- Set the standard headers for the message. This is just a convenient -- subprogram, since the same can be done by manipulating directly the -- headers. Charset is used for MIME encoding of the From: and Subject: -- headers only. procedure Set_From_Header (Msg : in out Message'Class; From_Email : String; From_Real_Name : String; Charset : String); -- Create and set a From: header for Msg using the given email address and -- real name. The real name has the indicated Charset. type Header_Filter is access function (H : Header'Class) return Boolean; -- A filter for headers. It is returned True, the header will be displayed, -- otherwise it is skipped. type Payload_Filter is access function (Attachment : Message'Class) return Boolean; -- Whether a given payload part should be displayed when a message is -- converted to a string. If it returns True, that part is displayed. -- When the filter is unspecified to To_String, all payloads are output. -- This filter only applies in the case of multipart messages, and only to -- the toplevel attachments (i.e. if an attachment is itself a message with -- other attachments, the filter will not be applied for these). procedure To_String (Msg : Message'Class; Envelope : Boolean := False; Header_Max_Line_Len : Positive := Default_Max_Header_Line_Length; Subject_Max_Line_Len : Positive := Default_Max_Header_Line_Length; Content_Filter : Payload_Filter := null; Filter : Header_Filter := null; Decode : Boolean := False; Quote_From : Boolean := False; Result : out Unbounded_String); -- Return the message as string. This string is suitable for passing to any -- program like sendmail to forward the mail to its recipients. -- If Envelope is True, the envelope line, if known, is included. -- If Content_Filter is specified, it can be used to filter out which part -- of multipart message should be displayed. -- If Filter is specified, it can be used to filter out which headers -- should be displayed. -- If Decode is True and this message is MIME-encoded, it is automatically -- decoded. -- If Quote_From is true, then each line of Msg's payload preceded by a -- blank line and starting with "From " will be prepended with ">" in order -- to avoid further tools to be confused with the From_ message delimiter. -- -- The message might be modified if for instance a boundary needs to be -- created or adjusted for a multipart message. procedure Set_Envelope_From (Msg : in out Message'Class; From : String); procedure Set_Envelope_From (Msg : in out Message'Class; Email : String; Local_Date : Ada.Calendar.Time); function Get_Envelope_From (Msg : Message'Class) return String; -- Set the "From " line used for the envelope of the message function Date_From_Envelope (Msg : Message'Class) return Ada.Calendar.Time; -- Return the date read in the envelope of the message. It is recommended -- that you get the date from the 'Date:' header when available instead. function Sender_From_Envelope (Msg : Message'Class) return String; -- Return the sender part of the envelope. It is recommended that you use -- the From: header instead when available procedure Add_Header (Msg : in out Message'Class; H : Header'Class); -- Set the unparsed block of headers for the message. -- If there is already a header with the same name, it isn't overridden. -- Instead, two headers with the same name will exist for the message. procedure Delete_Headers (Msg : Message'Class; Name : String); procedure Delete_Header (Msg : Message'Class; H : Header'Class); -- Delete either all headers with the given name (all if Name is the empty -- string), or a specific header. procedure Replace_Header (Msg : Message'Class; H : Header'Class); -- Replace the first header with the same name by H, and delete all other -- headers with the same name. This is different from doing a -- Delete_Headers (Msg, Name); -- Add_Header (Create (Name, ...)); -- since Replace_Header will preserve the order of headers. -- If no header with the same name is found, H is simply added to the list. function Get_Header (Msg : Message'Class; Name : String) return Header; -- Return the first header of Msg with the given name. If this header -- occurs multiple times, only the first occurrence is returned. -- Name is case-insensitive function Get_Type (H : Header) return String; -- For a header H that is a Content-Type or Content-Disposition, return -- the content type or the disposition type (i.e. the initial part of the -- header, before the semicolon). The returned value is always converted -- to lower case. For a null header, an empty string is returned. function Get_Content_Type (Msg : Message'Class) return String; -- Return the MIME content type for the message. -- As per RFC 2045, there is always such a content type, even if it wasn't -- specified explicitly by the headers. It defaults to text/plain when the -- message is not part of the payload of a multipart/report message, to -- message/rfc822 otherwise. The returned value is always converted to -- lower case. function Get_Message_Id (Msg : Message) return String; -- Return the Message_Id for this message. This returns the empty string if -- no such Id is defined. Otherwise, this extracts the Id from that header, -- properly keeping only the Id itself, and not the surrounding <..> if -- they exist. function Get_Date (Msg : Message) return Ada.Calendar.Time; -- Return the date the message was sent. This information is taken from the -- Date: header if it exists, and if not from the envelope of the message. function Size (Msg : Message; Include_Attachments : Boolean) return Long_Integer; -- Return the size of the message and all its MIME parts. This size is not -- extremely precise (and doesn't reflect the size it would take to convert -- it to a string for instance), and for instance doesn't include the size -- of the headers. -- If Include_Attachments is False, then all but the first text/plain part -- will be ignored type Encoding_Type is (Encoding_7bit, Encoding_8bit, Encoding_Binary, Encoding_QP, Encoding_Base64); function Get_Encoding_Type (Msg : Message'Class) return Encoding_Type; -- Return the encoding used for this message. -- As per RFC 2045, there is always such an encoding, and if no header is -- specified then Encoding_7bit is assumed. type Header_Iterator is private; function Get_Headers (Msg : Message'Class; Name : String := "") return Header_Iterator; -- Iterate over all headers with the given name. If Name is unspecified, -- iterates over all headers of the message. For Null_Message, return an -- empty iterator. Looping over all headers is done as follows: -- Iter := Get_Headers (Msg); -- loop -- Next (Iter, H); -- exit when H = Null_Header; -- Header_Processing (H); -- end loop; procedure Next (Iter : in out Header_Iterator; H : out Header); -- Returns current header if exists or Null_Header otherwise. -- Move to the next header with the expected name. function Next (Iter : in out Header_Iterator; H : out Header) return Boolean; -- Returns True if the header exists and returns it in H parameter. -- Move cursor position to the next header. The loop over headers could be -- like this: -- -- Iter := Get_Headers (Msg); -- while Next (Iter, H) loop -- Do_Something_With (H); -- end loop; ------------- -- Payload -- ------------- -- A message can either be a single part message, ie it just contains text, -- possibly in various charsets or a multi part message, in which case it -- can have attached files, contain nested messages, etc. -- The content of the message, whether single or multi part, is called the -- payload. -- Since each part of a multi-part message can itself have its own headers -- and be a nested message, the actual payload of a message is represented -- as a list of messages. function Is_Multipart (Msg : Message'Class) return Boolean; -- Whether the message contains several parts, and must be encoded as a -- multipart email message. If False, the payload is a simple string. Multipart_Error : exception; -------------------------- -- Single part messages -- -------------------------- procedure Set_Text_Payload (Msg : Message'Class; Payload : Unbounded_String; MIME_Type : String := Text_Plain; Disposition : String := ""; Charset : String := Charset_US_ASCII; Prepend : Boolean := False); -- Set the payload of the message, as text. No parsing is done. -- If the message is a single part message, this is the text of the -- message. If the message is a multi-part message, this is set as one of -- the parts, with the given MIME type. As a result, it can be called -- several times in such a case, each time will create a new part. -- If MIME_Type is set to the empty string, it is not updated in the -- message. This is mostly useful when Msg was parsed through one of the -- functions in Email.Parser. -- If Disposition is specified, it is used as the value of the -- Content-Disposition header of the text part. -- When Msg is a multi-part message, the new part is either appended after -- the existing parts, or prepend before, depending on the Prepend -- parameter. If Msg is a single part message, then Payload will replace -- the current payload if Prepend is False, otherwise the old payload is -- preserved and set after the new one. procedure Set_Text_Payload (Msg : Message'Class; Payload : String; MIME_Type : String := Text_Plain; Disposition : String := ""; Charset : String := Charset_US_ASCII; Prepend : Boolean := False); -- The same like above but Payload is just a String procedure Get_Single_Part_Payload (Msg : Message'Class; Payload : out Unbounded_String; Decode : Boolean := False); -- Return the content of a message when it doesn't contain multiparts. -- If this is a multipart message, Multipart_Error is raised. -- If Decode is true and this message is MIME-encoded, it is automatically -- decoded. You can also decode it later through the subprograms in -- email-utils.ads ------------------------- -- Multi part messages -- ------------------------- type Payload_Iterator is private; function Get_Payload (Msg : Message'Class) return Payload_Iterator; -- Return an iterator over the whole content of the message. -- If the message is not a multipart message, a single element will ever -- be returned, which is Msg itself. This allows for traversing both -- single parts and multiparts messages in a single piece of code. -- The following code will find all textual contents of Msg: -- Iter := Get_Payload (Msg); -- loop -- Next (Iter, Item => Attachment); -- exit when Attachment = Null_Message; -- if Get_Main_Type (Get_Content_Type (Attachment)) = "text" then -- Get_Single_Part_Payload (Attachment, ....); -- end if; -- end loop; procedure Next (Iter : in out Payload_Iterator; Item : out Message); -- Get the next part in the payload of a message. Null_Message is -- returned when there are no more parts in the message. procedure Delete_Payload (Msg : in out Message'Class; Iter : in out Payload_Iterator); -- Remove the corresponding payload from the message procedure Convert_To_Multipart (Msg : Message'Class); -- If Msg is a single part message, convert it to a multipart/mixed whose -- first part is the original payload, else do not change the MIME -- structure of Msg (but make sure that the underlying data structure is -- suitable for storage of a multipart message). procedure Convert_To_Multipart (Msg : Message'Class; MIME_Type : String; Force : Boolean := False); -- If Msg is a single part message, convert it to a multipart with the -- indicated MIME_Type, whose first part is the original payload. Also -- do so if Msg is a multipart message if it has a different MIME subtype, -- or if Force is True. Else do not change the MIME structure of Msg -- (but make sure that the underlying data structure is suitable -- for storage of a multipart message). procedure Convert_To_Single_Part (Msg : in out Message'Class; Purge : Boolean := False); -- Try to convert Msg to a single part message. This is only doable if -- there is a single textual part, or the message is already single part. -- If Msg contains a single part which is in turn a multipart Msg, it gets -- processed as well. -- All other cases will do nothing, unless Purge is set True, in which -- case all contents are lost, and the (single part) payload is reset -- to an empty text/plain part. procedure Set_Preamble (Msg : in out Message'Class; Preamble : String); -- Set the preamble of the MIME message. -- This text will be inserted before the first boundary, i.e. the first -- attached file. -- Normally, in MIME aware mailers, this preamble will not be visible. It -- will only be visible by viewing the full text of the message. -- If the message was single-part message, it is automatically converted to -- a multi-part message. procedure Set_Epilogue (Msg : in out Message'Class; Epilogue : String); -- This is similar to the preamble, but appears after the end of the -- last document. -- If the message was single-part message, it is automatically converted to -- a multi-part message procedure Add_Payload (Msg : in out Message'Class; Payload : Message; First : Boolean := False); -- Add a new part to a multipart message. Msg is first converted to -- multipart if necessary. Payload itself is stored in Msg, i.e. modifying -- Payload later on will impact Msg. This procedure cannot be used when -- attaching a real mail message, see Attach_Msg instead. -- If First is True, then add the new part at the beginning. Otherwise, -- add it at the end. procedure Attach_Msg (Msg : in out Message'Class; Attach : Message'Class; Description : String := ""); -- Attach an existing mail message to another one (for instance when -- forwarding as attachment). type Disposition_Type is (Disposition_Attachment, Disposition_Inline); procedure Attach (Msg : in out Message'Class; Path : GNATCOLL.VFS.Virtual_File; MIME_Type : String := Application_Octet_Stream; Recommended_Filename : GNATCOLL.VFS.Virtual_File := GNATCOLL.VFS.No_File; Description : String := ""; Charset : String := Charset_US_ASCII; Disposition : Disposition_Type := Disposition_Attachment; Encoding : Encoding_Type := Encoding_Base64); -- Attach a file to the payload. The file is immediately read from the -- disk, and encoded as necessary, so this might be an expensive operation -- to perform. -- Name_Error is raised if the file is not found. function Get_Boundary (Msg : Message'Class) return String; -- Return the boundary used for Msg to separate its various parts. -- The empty string is returned if this isn't a multipart message. procedure Set_Boundary (Msg : Message'Class; Boundary : String := ""); -- Set the boundary to use between parts of the message. If the empty -- string is passed, a boundary will be added if none already exists, or -- if the current one can not be used because some part of the message -- already includes it. -- The message is automatically converted to a multipart message if you -- call this message, since boundaries can not be used with single part -- messages. -- As per RFC 1521, the boundary can only use the following characters: -- 0-9 a-z A-Z '()+_,-./:=? -- In this implementation, it must include the sequence =_. This is a -- sequence that is guaranteed to never appear in quoted-printable or -- base64 encoded parts, and this implementation takes advantage of this -- to speed up the check that the boundary can be used. -- The string =_ will be appended as many times as necessary to Boundary to -- make it valid. -- In general, you do not need to call this procedure, which is called -- automatically when needed. private type Header_Record is record Name : Unbounded_String; Value : Charset_String_List.List; Ref_Count : Natural := 1; end record; type Header_Access is access Header_Record; type Header is new Ada.Finalization.Controlled with record Contents : Header_Access; end record; procedure Adjust (H : in out Header); procedure Finalize (H : in out Header); -- Headers are stored in a list, since order might be relevant sometimes, -- especially for the 'Received:' headers. package Header_List is new Ada.Containers.Doubly_Linked_Lists (Header); type Header_Iterator is record Cursor : Header_List.Cursor; Name : Unbounded_String; end record; type Message_Record; type Message_Access is access Message_Record; type Message is new Ada.Finalization.Controlled with record Contents : Message_Access; end record; -- Smart pointer to message. This provides automatic freeing of the memory, -- but allows us to have a list of messages without having access to the -- full view of a Message, which itself contains an instance of the list. procedure Adjust (Msg : in out Message); procedure Finalize (Msg : in out Message); package Message_List is new Ada.Containers.Doubly_Linked_Lists (Message); type Payload_Iterator is record Cursor : Message_List.Cursor; Msg : Message; end record; type Message_Payload (Multipart : Boolean := False) is record case Multipart is when True => Parts : Message_List.List; Preamble : Unbounded_String; Epilogue : Unbounded_String; when False => Text : Unbounded_String; end case; end record; Null_Payload : constant Message_Payload := (False, Null_Unbounded_String); Null_Multipart_Payload : constant Message_Payload := (True, Message_List.Empty_List, Null_Unbounded_String, Null_Unbounded_String); type Message_Record is record Ref_Count : Natural := 1; Envelope_From : Unbounded_String; Headers : Header_List.List; Payload : Message_Payload; Is_Nested : Boolean := False; end record; Null_Message : constant Message := (Ada.Finalization.Controlled with Contents => null); function Next_Occurrence (S : String; Char : Character; Skip_Quotes : Boolean := False) return Integer; -- Return the index of the next occurrence of Char, or a number greater -- than S'Last if we are on the last line. -- If Skip_Quotes is true, characters between a "..." will be ignored. function Is_Whitespace (Char : Character) return Boolean; pragma Inline (Is_Whitespace); -- Whether Char is a whitespace (tab or space) procedure Skip_Whitespaces (S : String; Index : in out Integer); pragma Inline (Skip_Whitespaces); -- Skip any whitespace character, including newlines, starting at Index. -- Leaves Index on the first non-whitespace character Null_Header : constant Header := (Ada.Finalization.Controlled with null); Null_Charset_String : constant Charset_String := (Null_Unbounded_String, Null_Unbounded_String); Null_Address : constant Email_Address := (Null_Unbounded_String, Null_Unbounded_String); end GNATCOLL.Email; gnatcoll-core-21.0.0/src/gnatcoll-storage_pools.ads0000644000175000017500000000377013661715457022154 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2005-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- The children packages provide various implementation of storage -- pools, that have been found useful over time. -- You might also be interested in GNAT.Debug_Pools, which is provided in -- the GNAT runtime itself. package GNATCOLL.Storage_Pools is pragma Pure; end GNATCOLL.Storage_Pools; gnatcoll-core-21.0.0/src/gnatcoll-coders-base64.adb0000644000175000017500000002506613743647711021614 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package body GNATCOLL.Coders.Base64 is -- The base64 character set Base64 : constant array (Base64_Mode) of aliased Base64_Encode_Array := (MIME => ('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', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'), URL => ('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', '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', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_')); Shift_1_2 : constant array (1 .. 2, 1 .. 2) of Integer := (1 => (1 => 4, 2 => 4), 2 => (1 => 2, 2 => 6)); -- Shifts for 1 and 2 states of the decoding type Decoder_Array is array (Stream_Element) of Unsigned_8; function Make_Decoder return Decoder_Array; ------------------ -- Make_Decoder -- ------------------ function Make_Decoder return Decoder_Array is begin return Result : Decoder_Array := (others => Unsigned_8'Last) do for J in Base64 (MIME)'Range loop Result (Character'Pos (Base64 (MIME) (J))) := J; end loop; for J in Base64_Encode_Array'Last - 1 .. Base64_Encode_Array'Last loop Result (Character'Pos (Base64 (URL) (J))) := J; end loop; end return; end Make_Decoder; Decoder : constant Decoder_Array := Make_Decoder; ---------------- -- Initialize -- ---------------- procedure Initialize (Coder : in out Encoder_Type; Wrap : Natural := 0; Mode : Base64_Mode := MIME) is begin Coder := Encoder_Type' (To_Char => Base64 (Mode)'Access, Align => Mode = MIME, Wrap => (if Wrap > 0 then Wrap + 1 else 0), others => <>); end Initialize; --------------- -- Transcode -- --------------- overriding procedure Transcode (Coder : in out Encoder_Type; In_Data : Stream_Element_Array; In_Last : out Stream_Element_Offset; Out_Data : out Stream_Element_Array; Out_Last : out Stream_Element_Offset; Flush : Flush_Mode) is procedure Append (Item : Unsigned_16) with Inline; procedure Append (Item : Character); procedure Append_EOL; ------------ -- Append -- ------------ procedure Append (Item : Character) is begin Out_Last := Out_Last + 1; Out_Data (Out_Last) := Character'Pos (Item); Coder.Out_Count := Coder.Out_Count + 1; end Append; procedure Append (Item : Unsigned_16) is begin Append (Coder.To_Char (Unsigned_8 (Item))); end Append; procedure Append_EOL is begin Out_Last := Out_Last + 1; Out_Data (Out_Last) := Character'Pos (ASCII.LF); Coder.Lines := Coder.Lines + 1; end Append_EOL; begin Out_Last := Out_Data'First - 1; In_Last := In_Data'First - 1; while Out_Last < Out_Data'Last loop if Coder.Wrap > 0 and then Coder.Out_Count > 0 and then (Coder.Out_Count + Coder.Lines) rem Stream_Element_Count (Coder.Wrap) = 0 then Append_EOL; elsif Coder.Left_Bits >= 6 then Coder.Left_Bits := Coder.Left_Bits - 6; Append (Shift_Right (Coder.Left, Coder.Left_Bits) and 16#3F#); elsif Coder.Finish and then Coder.Left_Bits = 0 then if not Coder.Align or else Coder.Out_Count rem 4 not in 2 .. 3 then if Coder.Wrap > 0 then Append_EOL; Coder.Wrap := 0; end if; exit; end if; Append ('='); elsif In_Last = In_Data'Last then exit when Coder.Finish or else Flush /= Finish; Coder.Finish := True; case Coder.Left_Bits is when 0 => null; when 2 => Append (Shift_Left (Coder.Left and 3, 4)); when 4 => Append (Shift_Left (Coder.Left and 16#F#, 2)); when others => raise Program_Error with "invalid Base64 encoder state"; end case; Coder.Left_Bits := 0; elsif Coder.Left_Bits < 6 then In_Last := In_Last + 1; Coder.Left_Bits := Coder.Left_Bits + 8; Coder.Left := Shift_Left (Coder.Left, 8) or Unsigned_16 (In_Data (In_Last)); end if; end loop; end Transcode; ------------- -- Is_Open -- ------------- overriding function Is_Open (Coder : Encoder_Type) return Boolean is begin return Coder.To_Char /= null; end Is_Open; -------------- -- Total_In -- -------------- overriding function Total_In (Coder : Coder_Type) return Stream_Element_Count is begin return Coder.In_Count; end Total_In; --------------- -- Total_Out -- --------------- overriding function Total_Out (Coder : Encoder_Type) return Stream_Element_Count is begin return Coder.Out_Count + Coder.Lines - 1; end Total_Out; -------------- -- Finished -- -------------- overriding function Finished (Coder : Encoder_Type) return Boolean is begin return Coder.Finish and then Coder.Left_Bits = 0 and then Coder.Wrap = 0 and then (not Coder.Align or else Coder.Out_Count rem 4 not in 2 .. 3); end Finished; ----------- -- Close -- ----------- overriding procedure Close (Coder : in out Encoder_Type) is begin Coder.To_Char := null; end Close; ---------------- -- Initialize -- ---------------- procedure Initialize (Coder : in out Decoder_Type) is begin Coder := Decoder_Type'(others => <>); Coder.Open := True; end Initialize; --------------- -- Transcode -- --------------- overriding procedure Transcode (Coder : in out Decoder_Type; In_Data : Stream_Element_Array; In_Last : out Stream_Element_Offset; Out_Data : out Stream_Element_Array; Out_Last : out Stream_Element_Offset; Flush : Flush_Mode) is Bits : Unsigned_8; State : Integer; procedure Append (Item : Unsigned_8); ------------ -- Append -- ------------ procedure Append (Item : Unsigned_8) is begin Out_Last := Out_Last + 1; Out_Data (Out_Last) := Stream_Element (Item); Coder.Out_Count := Coder.Out_Count + 1; end Append; begin Out_Last := Out_Data'First - 1; In_Last := In_Data'First - 1; while Out_Last < Out_Data'Last loop if In_Last = In_Data'Last then exit when Flush /= Finish or else Coder.Finish; Coder.Finish := True; if Coder.Has then Append (Coder.Bits); Coder.Has := False; end if; exit; else In_Last := In_Last + 1; Bits := Decoder (In_Data (In_Last)); end if; if Bits /= Unsigned_8'Last then State := Integer (Coder.In_Count rem 4); case State is when 0 => Coder.Bits := Shift_Left (Bits, 2); Coder.Has := True; when 1 | 2 => Append (Coder.Bits or Shift_Right (Bits, Shift_1_2 (State, 1))); Coder.Bits := Shift_Left (Bits, Shift_1_2 (State, 2)); Coder.Has := False; when others => Append (Coder.Bits or Bits); Coder.Has := False; end case; Coder.In_Count := Coder.In_Count + 1; end if; end loop; end Transcode; ------------- -- Is_Open -- ------------- overriding function Is_Open (Coder : Decoder_Type) return Boolean is begin return Coder.Open; end Is_Open; --------------- -- Total_Out -- --------------- overriding function Total_Out (Coder : Decoder_Type) return Stream_Element_Count is begin return Coder.Out_Count; end Total_Out; -------------- -- Finished -- -------------- overriding function Finished (Coder : Decoder_Type) return Boolean is begin return Coder.Finish and not Coder.Has; end Finished; ----------- -- Close -- ----------- overriding procedure Close (Coder : in out Decoder_Type) is begin Coder.Open := False; end Close; end GNATCOLL.Coders.Base64; gnatcoll-core-21.0.0/src/gnatcoll-any_types.ads0000644000175000017500000000622613661715457021306 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides a few utilities to manipulate types that can have -- multiple forms. In particular this is used to provide a convenient way -- of manipulating Python objects, without having the need to manipulate -- PyObject in the Ada code. -- See GNATCOLL.Any_Types.Python. with Ada.Unchecked_Deallocation; with Interfaces.C; package GNATCOLL.Any_Types is -------------- -- Any_Type -- -------------- -- This type provides an Ada encapsulation of certain types type Types is (No_Type, Integer_Type, String_Type, Tuple_Type, List_Type); type Any_Type; type Any_Type_Access is access Any_Type; type Any_Type_Array is array (Natural range <>) of Any_Type_Access; type Any_Type (T : Types; Length : Natural) is record case T is when No_Type => null; when Integer_Type => Int : Interfaces.C.long; when String_Type => Str : String (1 .. Length); when Tuple_Type => Tuple : Any_Type_Array (1 .. Length); when List_Type => List : Any_Type_Array (1 .. Length); end case; end record; procedure Free (X : in out Any_Type); -- Free memory associated to X Empty_Any_Type : constant Any_Type := (No_Type, 0); private procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Any_Type, Any_Type_Access); end GNATCOLL.Any_Types; gnatcoll-core-21.0.0/src/gnatcoll-plugins.ads0000644000175000017500000000512613661715457020752 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with System; package GNATCOLL.Plugins is type Plugin is private; No_Plugin : constant Plugin; function Load (Path : String) return Plugin; -- Attempts to load the plugin located at Path. -- Returns No_Plugin if plugin not found. function Routine_Address (P : Plugin; Name : String) return System.Address; -- Returns address of the routine named Unit_Name defined in the plugin P. -- Returns Null_Address if such routine not found in the plugin. function Last_Error_Message return String; -- Returns last error message in case of Load or Routine_Address returned -- empty result. procedure Unload (P : in out Plugin); -- Remove the plugin from service. Note the actual effect is -- operating-system dependent. private type Plugin is new System.Address; No_Plugin : constant Plugin := Plugin (System.Null_Address); end GNATCOLL.Plugins; gnatcoll-core-21.0.0/src/gnatcoll-mmap.ads0000644000175000017500000004371513661715457020231 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2007-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides memory mapping of files. Depending on your operating -- system, this might provide a more efficient method for accessing the -- contents of files. -- A description of memory-mapping is available on the sqlite page, at: -- http://www.sqlite.org/mmap.html -- -- The traditional method for reading a file is to allocate a buffer in the -- application address space, then open the file and copy its contents. When -- memory mapping is available though, the application asks the operating -- system to return a pointer to the requested page, if possible. If the -- requested page has been or can be mapped into the application address -- space, the system returns a pointer to that page for the application to -- use without having to copy anything. Skipping the copy step is what makes -- memory mapped I/O faster. -- -- When memory mapping is not available, this package automatically falls -- back to the traditional copy method. -- -- Example of use for this package, when reading a file that can be fully -- mapped -- -- declare -- File : Mapped_File; -- Str : Str_Access; -- begin -- File := Open_Read ("/tmp/file_on_disk"); -- Read (File); -- read the whole file -- Str := Data (File); -- for S in 1 .. Last (File) loop -- Put (Str (S)); -- end loop; -- Close (File); -- end; -- -- When the file is big, or you only want to access part of it at a given -- time, you can use the following type of code. -- declare -- File : Mapped_File; -- Str : Str_Access; -- Offs : File_Size := 0; -- Page : constant Integer := Get_Page_Size; -- begin -- File := Open_Read ("/tmp/file_on_disk"); -- while Offs < Length (File) loop -- Read (File, Offs, Length => Long_Integer (Page) * 4); -- Str := Data (File); -- -- -- Print characters for this chunk: -- for S in Integer (Offs - Offset (File)) + 1 .. Last (File) loop -- Put (Str (S)); -- end loop; -- -- -- Since we are reading multiples of Get_Page_Size, we can simplify -- -- with -- -- for S in 1 .. Last (File) loop ... -- -- Offs := Offs + Long_Integer (Last (File)); -- end loop; with Ada.Unchecked_Conversion; with Interfaces.C; with GNAT.Strings; use GNAT.Strings; with GNATCOLL.Strings; with System; package GNATCOLL.Mmap is type Mapped_File is private; -- File to be mapped in memory. -- This package will use the fastest possible algorithm to load the -- file in memory. On systems that support it, the file is not really -- loaded in memory. Instead, a call to the mmap() system call (or -- CreateFileMapping()) will keep the file on disk, but make it -- accessible as if it was in memory. -- When the system does not support it, the file is actually loaded in -- memory through calls to read(), and written back with write() when you -- close it. This is of course much slower. -- Legacy: each mapped file has a "default" mapped region in it. type Mapped_Region is private; -- A representation of part of a file in memory. Actual reading/writing -- is done through a mapped region. After being returned by Read, a mapped -- region must be free'd when done. If the original Mapped_File was open -- for reading, it can be closed before the mapped region is free'd. Invalid_Mapped_File : constant Mapped_File; Invalid_Mapped_Region : constant Mapped_Region; type File_Size is new Interfaces.C.size_t; function Open_Read (Filename : String; Use_Mmap_If_Available : Boolean := True) return Mapped_File; -- Open a file for reading. The same file can be shared by multiple -- processes, that will see each others' changes as they occur. -- Any attempt to write the data might result in a segmentation fault, -- depending on how the file is open. -- Name_Error is raised if the file does not exist. -- Filename should be compatible with the filesystem. function Open_Write (Filename : String; Use_Mmap_If_Available : Boolean := True) return Mapped_File; -- Open a file for writing. -- You cannot change the length of the file. -- Name_Error is raised if the file does not exist -- Filename should be compatible with the filesystem. procedure Close (File : in out Mapped_File); -- Close the file, and unmap the memory that is used for the region -- contained in File. If the system does not support the unmmap() system -- call or equivalent, or these were not available for the file itself, -- then the file is written back to the disk if it was opened for writing. procedure Free (Region : in out Mapped_Region); -- Unmap the memory that is used for this region and deallocate the region type Use_Advice is (Use_Normal, Use_Random, Use_Sequential); for Use_Advice'Size use Interfaces.C.int'Size; for Use_Advice use (Use_Normal => 1, Use_Random => 2, Use_Sequential => 4); -- This type can be used to provide advice to some operation systems on -- how a mapped page will be used. -- -- If you specify Use_Sequential, you are telling the system that the -- contents of the page will be read sequentially from lower to higher -- address, and therefore the system should use prefetching aggressively. -- -- If you specify Use_Random, the page will be accessed in a -- non-sequential manner. -- -- This advice might be ignored by the system (depending on whether the -- madvise() system call is supported). It will always be ignored for -- systems that do not support mmap. procedure Read (File : Mapped_File; Region : in out Mapped_Region; Offset : File_Size := 0; Length : File_Size := 0; Mutable : Boolean := False; Advice : Use_Advice := Use_Normal); -- Read a specific part of File and set Region to the corresponding mapped -- region, or re-use it if possible. -- Offset is the number of bytes since the beginning of the file at which -- we should start reading. Length is the number of bytes that should be -- read. If set to 0, as much of the file as possible is read (presumably -- the whole file unless you are reading a _huge_ file). -- Note that no (un)mapping is is done if that part of the file is already -- available through Region. -- If the file was opened for writing, any modification you do to the -- data stored in File will be stored on disk (either immediately when the -- file is opened through a mmap() system call, or when the file is closed -- otherwise). -- Mutable is processed only for reading files. If set to True, the -- data can be modified, even through it will not be carried through the -- underlying file, nor it is guaranteed to be carried through remapping. -- This function takes care of page size alignment issues. The accessors -- below only expose the region that has been requested by this call, even -- if more bytes were actually mapped by this function. -- -- TODO??? Enable to have a private copy for readable files -- -- Operating systems generally limit the number of open file descriptors -- that an application can have at one time (typically 1024 or 2048). -- They however often have a much higher limit on the number of mapped -- regions (65535 for instance). If you hitting the first limit, you -- could use the following workflow: -- -- File := Open_Read ("filename.txt"); -- Region := Read (File); -- Close (File); -- release the file descriptor -- ... -- Free (Region); -- release the mapped file function Read (File : Mapped_File; Offset : File_Size := 0; Length : File_Size := 0; Mutable : Boolean := False; Advice : Use_Advice := Use_Normal) return Mapped_Region; -- Likewise, return a new mapped region procedure Read (File : Mapped_File; Offset : File_Size := 0; Length : File_Size := 0; Mutable : Boolean := False) with Obsolescent; -- Likewise, use the legacy "default" region in File function Length (File : Mapped_File) return File_Size with Inline; -- Size of the file on the disk function Offset (Region : Mapped_Region) return File_Size with Inline; -- Return the offset, in the physical file on disk, corresponding to the -- requested mapped region. The first byte in the file has offset 0. function Offset (File : Mapped_File) return File_Size with Inline, Obsolescent; -- Likewise for the region contained in File function Data_Address (Region : Mapped_Region) return System.Address with Inline; function Data_Address (File : Mapped_File) return System.Address with Inline, Obsolescent; -- Return the address of the internal buffer. -- Do not use this function directly, but via an instance of the -- package Data_Getters below. function Data_Size (Region : Mapped_Region) return File_Size with Inline; function Data_Size (File : Mapped_File) return File_Size with Inline, Obsolescent; -- Full size of the mapped region. -- Better to use one of the instances of Data_Getters instead. generic type Index_Type is range <>; -- The type of indexes used when mapping the file to memory. -- Typical values are 'Positive' when you want to read files less than -- 2Gb in size, although you might want to use -- System.Storage_Elements.Storage_Offset or Long_Long_Integer on -- 64 bits system supporting the mmap system call (which will allow -- you to manipulate Petabytes files...) type Base_Unconstrained_String is array (Index_Type range <>) of Character; -- How is memory represented. -- For small strings, it is recommended to use the String type -- directly for ease of use for the user. package Data_Getters is pragma Compile_Time_Error (Index_Type'First /= 1, "Wrong index type"); subtype Extended_Index_Type is Index_Type'Base range 0 .. Index_Type'Last; subtype Unconstrained_String is Base_Unconstrained_String (Index_Type); type Str_Access is access all Unconstrained_String; pragma No_Strict_Aliasing (Str_Access); -- We do not use a String, which would limit the index to Integer and -- not allow us to load files larger than 2Gb. -- We also do not systematically use a -- System.Storage_Elements.Storage_Array, since it is easier for users -- if we directly have Character elements rather than Storage_Element. function Convert is new Ada.Unchecked_Conversion (System.Address, Str_Access); function To_Str_Access (Str : GNAT.Strings.String_Access) return Str_Access is (if Str = null then null else Convert (Str.all'Address)); -- Convert Str. The returned value points to the same memory block, -- but no longer includes the bounds, which you need to manage yourself function Last (Region : Mapped_Region) return Extended_Index_Type is (Extended_Index_Type (Data_Size (Region))); -- Return the number of requested bytes mapped in this region. It is -- erroneous to access Data for indices outside 1 .. Last (Region). -- Such accesses may cause Storage_Error to be raised. -- -- A constraint error is raised if the size of the region is larger -- than can be represented by Index_Type. So you need to pass a -- compatible Length parameter in your call to Open_Read. function Last (File : Mapped_File) return Extended_Index_Type is (Extended_Index_Type (Data_Size (File))) with Obsolescent; -- Return the number of requested bytes mapped in the region contained -- in File. It is erroneous to access Data for indices outside -- of 1 .. Last (File); such accesses may cause Storage_Error to -- be raised. function Data (Region : Mapped_Region) return Str_Access is (Convert (Data_Address (Region))); -- The data mapped in Region as requested. The result is an -- unconstrained string, so you cannot use the usual 'First and -- 'Last attributes. Instead, these are respectively 1 and Size. function Data (File : Mapped_File) return Str_Access is (Convert (Data_Address (File))) with Obsolescent; -- Likewise for the region contained in File end Data_Getters; package Short is new Data_Getters (Positive, String); -- This package can be used when mapping files less than 2Gb. -- A range of the result of Data can be converted to a String, as in: -- S : constant String := String (Data (Region) (1 .. Last (Region))); subtype Long_Index is Long_Long_Integer range 1 .. Long_Long_Integer'Last; type Large_Unconstrained_String is array (Long_Index range <>) of Character; package Long is new Data_Getters (Long_Index, Large_Unconstrained_String); -- This package can be used when mapping files up to a petabyte. -- The whole data cannot be represented as a single string, so you'll -- need to iterate on it. subtype Str_Access is Short.Str_Access; function "=" (Left, Right : Str_Access) return Boolean renames Short."="; function Last (Region : Mapped_Region) return Positive renames Short.Last; function Last (File : Mapped_File) return Positive renames Short.Last; function Data (Region : Mapped_Region) return Str_Access renames Short.Data; function Data (File : Mapped_File) return Str_Access renames Short.Data; -- Convenient renamings, for backward compatibility. -- These functions only work for files up to 2Gb. For larger sizes, -- you should use Long.Str_Access, Long.Last and Long.Data instead. function Is_Mutable (Region : Mapped_Region) return Boolean; -- Return whether it is safe to change bytes in Data (Region). This is true -- for regions from writable files, for regions mapped with the "Mutable" -- flag set, and for regions that are copied in a buffer. Note that it is -- not specified whether empty regions are mutable or not, since there is -- no byte no modify. function Is_Mmapped (File : Mapped_File) return Boolean with Inline; -- Whether regions for this file are opened through an mmap() system call -- or equivalent. This is in general irrelevant to your application, unless -- the file can be accessed by multiple concurrent processes or tasks. In -- such a case, and if the file is indeed mmap-ed, then the various parts -- of the file can be written simultaneously, and thus you cannot ensure -- the integrity of the file. If the file is not mmapped, the latest -- process to Close it overwrite what other processes have done. function Get_Page_Size return Positive; -- Returns the number of bytes in a page. Once a file is mapped from the -- disk, its offset and Length should be multiples of this page size (which -- is ensured by this package in any case). Knowing this page size allows -- you to map as much memory as possible at once, thus potentially reducing -- the number of system calls to read the file by chunks. function Read_Whole_File (Filename : String; Empty_If_Not_Found : Boolean := False) return GNAT.Strings.String_Access; function Read_Whole_File (Filename : String) return GNATCOLL.Strings.XString; -- Returns the whole contents of the file. -- The returned string must be freed by the user. -- This is a convenience function, which is of course slower than the ones -- above since we also need to allocate some memory, actually read the file -- and copy the bytes. -- If the file does not exist, null is returned. However, if -- Empty_If_Not_Found is True, then the empty string is returned instead. -- Filename should be compatible with the filesystem. -- -- This function only works for files smaller than 2Gb. private type Mapped_File_Record; type Mapped_File is access Mapped_File_Record; type Mapped_Region_Record; type Mapped_Region is access Mapped_Region_Record; Invalid_Mapped_File : constant Mapped_File := null; Invalid_Mapped_Region : constant Mapped_Region := null; end GNATCOLL.Mmap; gnatcoll-core-21.0.0/src/gnatcoll-symbols.adb0000644000175000017500000001605113661715457020737 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Containers; use Ada.Containers; with Ada.Strings.Hash; with Ada.Unchecked_Conversion; with Ada.Unchecked_Deallocation; with GNAT.IO; use GNAT.IO; with GNAT.Strings; with System.Address_Image; package body GNATCOLL.Symbols is use String_Htable; Table_Size : constant := 98_317; -- The initial capacity of the htable. This was computed from inserting -- all entities from the GPS project, using Ada.Strings.Hash for the hash, -- but seems to be the same when using other hash codes. -- The table will readjust itself anyway, but setting this properly avoids -- a few resizing. ----------------- -- Debug_Print -- ----------------- function Debug_Print (S : Symbol) return String is begin if S = No_Symbol then return ""; else return ""; end if; end Debug_Print; ---------- -- Hash -- ---------- function Hash (Str : Cst_String_Access) return Hash_Type is begin return Ada.Strings.Hash (Str.all); end Hash; --------------- -- Key_Equal -- --------------- function Key_Equal (Key1, Key2 : Cst_String_Access) return Boolean is begin return Key1.all = Key2.all; end Key_Equal; ---------- -- Find -- ---------- function Find (Table : access Symbol_Table_Record; Str : String) return Symbol is Result : String_Htable.Cursor; Tmp : Cst_String_Access; begin if Str'Length = 0 then return Empty_String; else Table.Calls_To_Find := Table.Calls_To_Find + 1; Result := Table.Hash.Find (Str'Unrestricted_Access); if not Has_Element (Result) then Table.Total_Size := Table.Total_Size + Str'Length; Tmp := new String'(Str); Table.Hash.Include (Tmp); return Symbol (Tmp); else Table.Size_Saved := Table.Size_Saved + Str'Length; end if; return Symbol (Element (Result)); end if; end Find; ------------------- -- Display_Stats -- ------------------- procedure Display_Stats (Self : access Symbol_Table_Record) is C : String_Htable.Cursor := Self.Hash.First; Tmp : Cst_String_Access; Count : Natural := 0; Last : Hash_Type := Hash_Type'Last; H : Hash_Type; Bucket_Count : Natural := 0; begin while Has_Element (C) loop Tmp := Element (C); H := Hash (Tmp); if H = Last then Count := Count + 1; else if Last /= Hash_Type'Last then Put_Line ("Bucket" & Last'Img & " =>" & Count'Img & " entries"); end if; Last := H; Count := 1; Bucket_Count := Bucket_Count + 1; end if; Put_Line (Hash (Tmp)'Img & " => " & Tmp.all); Next (C); end loop; Put_Line ("Total calls to Find: " & Self.Calls_To_Find'Img); Put_Line ("Number of entries in the symbols table:" & Self.Hash.Length'Img); Put_Line ("Maximum number of buckets:" & Self.Hash.Capacity'Img); Put_Line ("Number of buckets used:" & Bucket_Count'Img); Put_Line ("Mean entries per bucket:" & Integer'Image (Integer (Self.Hash.Length) / Bucket_Count)); Put_Line ("Total size in strings:" & Self.Total_Size'Img); Put_Line ("Size that would have been allocated for strings:" & Self.Size_Saved'Img); end Display_Stats; --------- -- Get -- --------- function Get (Sym : Symbol; Empty_If_Null : Boolean := True) return Cst_String_Access is begin if Sym = No_Symbol then if Empty_If_Null then return Cst_String_Access (Empty_String); else return null; end if; else return Cst_String_Access (Sym); end if; end Get; ---------- -- Free -- ---------- procedure Free (Table : in out Symbol_Table_Record) is function Convert is new Ada.Unchecked_Conversion (Cst_String_Access, GNAT.Strings.String_Access); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (String, GNAT.Strings.String_Access); S : GNAT.Strings.String_Access; C : String_Htable.Cursor := Table.Hash.First; Tmp : Cst_String_Access; begin while Has_Element (C) loop Tmp := Element (C); Next (C); S := Convert (Tmp); Unchecked_Free (S); end loop; Table.Hash.Clear; end Free; ---------- -- Free -- ---------- procedure Free (Table : in out Symbol_Table_Access) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Symbol_Table_Record'Class, Symbol_Table_Access); begin if Table /= null then Free (Table.all); Unchecked_Free (Table); end if; end Free; ---------- -- Hash -- ---------- function Hash (S : Symbol) return Hash_Type is begin return Hash (Cst_String_Access (S)); end Hash; -------------- -- Allocate -- -------------- function Allocate return Symbol_Table_Access is T : constant Symbol_Table_Access := new Symbol_Table_Record; begin T.Hash.Reserve_Capacity (Table_Size); return T; end Allocate; end GNATCOLL.Symbols; gnatcoll-core-21.0.0/src/gnatcoll-templates.ads0000644000175000017500000001562413661715457021273 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides support for replacing special substrings in a string. -- Typically, these are used to replace substrings like "%version" by the some -- other value, at run time. -- Do not confuse this unit with the templates parser which is provided in the -- context of the Ada Web Server (AWS), and which is used to parse external -- file and replace part of them. with GNAT.Strings; package GNATCOLL.Templates is type Substitution_Value is record Name : GNAT.Strings.String_Access; Value : GNAT.Strings.String_Access; end record; type Substitution_Array is array (Natural range <>) of Substitution_Value; procedure Free (Substrings : in out Substitution_Array); -- Free the memory occupied by the array No_Substitution : constant Substitution_Array; type Substitute_Callback is access function (Name : String; Quoted : Boolean) return String; -- A callback for Substitute below. It is called once for each '%...' -- parameter found in the string. Name doesn't include the delimiter. -- Quoted indicate whether the parameter was quoted, i.e. the '%...' was -- found as part of a quoted substring (for instance -- a "quoted %version substring" b -- The reason is that the substituted version could be different in such -- a case, and the substituted value might need to protect quote symbols -- in its replacement string. -- Should raise Invalid_Substitution if Name cannot be substituted Default_Delimiter : constant Character := '%'; -- The default delimiter used to mark the special substrings. It can be -- overridden in the various Substitute subprograms below. -- The special substrings always start with this delimiter, and including -- the following number or identifier. That identifier can be quoted -- between curly braces ({...}) or parentheses to avoid ambiguities. -- For instance: -- a%bcd*df => identifier name is "bcd" -- a%123ab => identifier name is "123" -- a%{bc}d*df => identifier name is "bc" -- a%(bc)d*df => identifier name is "bc" -- -- If the identifier is a number, the special character "-" will -- be included in the name if it follows the number exactly: -- a %1- b => identifier name is "1-" -- a %1+ b => identifier name is "1" -- The goal is to use this to indicate a range of parameter (in the first -- example above, the intended substitution is the value of %1 concatenated -- with that of %2, %3,..., so is an equivalent of "%1%2%3%4" if there are -- four possible parameters. -- -- If the first character after the delimiter is not an alphanumeric -- character, that will be the name of the identifier -- a %* b => identifier name is "*" -- a %^ b => identifier name is "^" -- -- When the delimiter is duplicated, it will always be replaced by a -- single instance of the delimiter (unless you have specified another -- explicit replacement for it in the Substrings parameter). For instance, -- if the string contains "a%%b" it will be replaced with "a%b". -- -- When an identifier is specified within curly braces or parentheses, a -- default value can be specified for it, which will be used if no other -- substitution is available. The syntax is similar to that of the Unix -- shell: -- %{var:-default} -- where "default" is the default value to use. type Error_Handling is (Keep_As_Is, Replace_With_Empty, Report_Error); -- What to do when no substitution value was found: -- If Keep_As_Is, the text is unaltered. "%invalid" remains as is -- If Replace_With_Empty, the text is replaced with the empty string. -- "%invalid" becomes "". -- If Report_Error, an exception Invalid_Substitution is raised function Substitute (Str : String; Substrings : Substitution_Array := No_Substitution; Callback : Substitute_Callback := null; Delimiter : Character := Default_Delimiter; Recursive : Boolean := False; Errors : Error_Handling := Keep_As_Is) return String; -- Replace all substrings in Str that start with Delimiter (see the -- declaration of Default_Delimiter for more information on identifier -- names). -- If an identifier found in Str matches no entry from Substrings, -- Callback is called to try and find the appropriate substitution. If -- that raises Invalid_Substitution, and the identifier contains a default -- value, it is used. -- If no substitution value was found, the behavior depends on the -- Error parameter. -- -- If Recursive is true, then this function will also substitute substrings -- in the values specified in Substrings, for instance: -- Delimiter := % -- Substrings (1) := (Name => "a", Value => "c%b") -- Substrings (2) := (Name => "b", Value => "d") -- -- Str := "%a" results in "cd" if Recursive is True -- results in "c%b" otherwise Invalid_Substitution : exception; private No_Substitution : constant Substitution_Array := (1 .. 0 => (null, null)); end GNATCOLL.Templates; gnatcoll-core-21.0.0/src/gnatcoll-boyer_moore.ads0000644000175000017500000000726613661715457021621 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2001-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package implements the Boyer-Moore algorithm for string searching, -- as described in the book "Algorithms" by T. Cormen (McGrawHill edts) -- -- This is a very efficient string searching algorithm, where the key being -- search is preprocessed to speed up further searches. However, unlike some -- other search algorithms, the text being searched does not need any -- pre-processing. package GNATCOLL.Boyer_Moore is type Pattern is private; Max_Pattern_Length : constant := Integer'Last; -- Maximal length for patterns that can be searched. -- Changing this means that patterns will simply use more space. procedure Compile (Motif : in out Pattern; From_String : String; Case_Sensitive : Boolean := True); -- Compile the required tables to match From_String anywhere. -- Motif needs to be freed when you are done using it. -- -- Note: A case_sensitive search is always more efficient, and should -- be used if you don't specifically need a case insensitive search. procedure Free (Motif : in out Pattern); -- Free the memory occupied by the motif function Search (Motif : Pattern; In_String : String) return Integer; -- Return the location of the match for Motif in In_String, or -1 if there -- is no match; private subtype Offset is Natural range 0 .. Max_Pattern_Length; -- This is the maximal offset reported by pattern. This might result in -- a slightly less efficient processing for patterns longer than this in -- extreme cases, but these are for very rare cases. type Occurrence_Array is array (Character) of Offset; type Offset_Array is array (Natural range <>) of Offset; type Offset_Array_Access is access Offset_Array; type String_Access is access String; type Pattern is record Last_Occurrence : Occurrence_Array; Good_Suffix : Offset_Array_Access; Motif : String_Access; Case_Sensitive : Boolean; end record; end GNATCOLL.Boyer_Moore; gnatcoll-core-21.0.0/src/gnatcoll-os-constants__osx.ads0000644000175000017500000000562313661715457022756 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L . O S . C O N S T A N T S -- -- -- -- Copyright (C) 2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This is the MacOS version of GNATCOLL.OS.Constants package package GNATCOLL.OS.Constants is pragma Pure; ----------------------- -- OS identification -- ----------------------- OS : constant OS_Type := MacOS; ------------------------------------- -- File system specific constants -- ------------------------------------- Dir_Sep : constant Character := '/'; -- The character that separates qualified filename components Path_Sep : constant Character := ':'; -- The character that separates paths in a path list Exe_Ext : constant String := ""; -- Executable image extension Default_Casing_Policy : constant Filename_Casing_Policy := Preserving; -- Default casing policy chosen by the OS ------------------------------------------------ -- Dynamic link libraries specific constants -- ------------------------------------------------ DLL_Name : constant String := "shared library"; -- The OS-specific term to refer to a DLL DLL_Search_Path_Var : constant String := "DYLD_LIBRARY_PATH"; -- Environment variable used to search for DLLs DLL_Ext : constant String := ".dylib"; -- DLL image extension end GNATCOLL.OS.Constants; gnatcoll-core-21.0.0/src/gnatcoll-vfs.adb0000644000175000017500000017341113661715457020051 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Calendar; use Ada.Calendar; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Numerics.Discrete_Random; with Ada.Exceptions; use Ada.Exceptions; with Ada.Tags; use Ada.Tags; with Ada.Strings.Hash; with Ada.Strings.Hash_Case_Insensitive; with Ada.Text_IO; with Ada.Unchecked_Conversion; with System; with GNAT.Heap_Sort; use GNAT.Heap_Sort; with GNATCOLL.Utils; use GNATCOLL.Utils; with GNATCOLL.IO; use GNATCOLL.IO; with GNATCOLL.IO.Remote; use GNATCOLL.IO.Remote; with GNATCOLL.Path; use GNATCOLL.Path; with GNATCOLL.Remote; use GNATCOLL.Remote; with GNATCOLL.Remote.Db; use GNATCOLL.Remote.Db; with GNATCOLL.VFS_Types; use GNATCOLL.VFS_Types; -- Cannot use GNATCOLL.Traces, or we end up with elaboration cycle package body GNATCOLL.VFS is Empty_String : aliased Filesystem_String := ""; Handle_Symbolic_Links : Boolean := GNAT.OS_Lib.Directory_Separator /= '\'; -- If this variable is False, we assume there is never any symbolic link, -- and thus we do not spend time resolving them. function "+" (S : Filesystem_String) return FS_String; function "+" (S : FS_String) return Filesystem_String; function "+" (S : FS_String) return String; pragma Inline ("+"); -- FS_String and Filesystem_String are identical in their intent. -- We just have a visibility issue as it really should be defined in -- VFS's spec, so can't be used by underneath packages (GNATCOLL.IO and -- GNATCOLL.Path) function "+" is new Ada.Unchecked_Conversion (FS_String_Access, Filesystem_String_Access); procedure Ensure_Normalized (File : Virtual_File'Class; Resolve_Symlinks : Boolean); -- Make sure that File.Value.Normalized is filled --------- -- "+" -- --------- function "+" (S : Filesystem_String) return FS_String is begin return FS_String (S); end "+"; function "+" (S : FS_String) return Filesystem_String is begin return Filesystem_String (S); end "+"; function "+" (S : FS_String) return String is begin return String (S); end "+"; function "+" (S : Filesystem_String) return String is begin return String (S); end "+"; function "+" (S : String) return Filesystem_String is begin return Filesystem_String (S); end "+"; --------- -- "=" -- --------- function Equal (S1, S2 : Filesystem_String) return Boolean is begin return Equal (+S1, +S2, Is_Case_Sensitive (Local_FS)); end Equal; --------- -- "=" -- --------- overriding function "=" (File1, File2 : Virtual_File) return Boolean is begin -- Test for the same pointer to actual value (or both null) if File1.Value = File2.Value then return True; -- Test if one of the values is null elsif File1.Value = null or else File2.Value = null or else File1.Value.all'Tag /= File2.Value.all'Tag then return False; -- Finally, we test the normalized paths else Ensure_Normalized (File1, Resolve_Symlinks => True); Ensure_Normalized (File2, Resolve_Symlinks => True); -- We also take care of potential trailing dir separator by enforcing -- them return Equal (File1.Value.Get_FS, Ensure_Directory (File1.Value.Get_FS, File1.Value.Normalized_And_Resolved.all), Ensure_Directory (File1.Value.Get_FS, File2.Value.Normalized_And_Resolved.all)); end if; end "="; --------- -- "=" -- --------- function "=" (Left : Writable_File; Right : Writable_File) return Boolean is use type GNAT.OS_Lib.File_Descriptor; begin -- Compare all components except Error for backward compatibility. return Left.File = Right.File and then Left.Tmp_File = Right.Tmp_File and then Left.FD = Right.FD and then Left.Append = Right.Append and then Left.Success = Right.Success; end "="; --------- -- "<" -- --------- function "<" (File1, File2 : Virtual_File) return Boolean is C1, C2 : Character; Ind1, Ind2 : Integer; Case_Sensitive : Boolean; begin if File1 = File2 then return False; elsif File1.Value = null then return True; elsif File2.Value = null then return False; elsif Is_Local (File1.Value.all) /= Is_Local (File2.Value.all) then return Is_Local (File1.Value.all); elsif not Is_Local (File1.Value.all) and then Get_Host (File1) /= Get_Host (File2) then return Get_Host (File1) < Get_Host (File2); else Case_Sensitive := Is_Case_Sensitive (File1.Value.Get_FS) and then Is_Case_Sensitive (File2.Value.Get_FS); Ensure_Normalized (File1, Resolve_Symlinks => True); Ensure_Normalized (File2, Resolve_Symlinks => True); if Case_Sensitive then return File1.Value.Normalized_And_Resolved.all < File2.Value.Normalized_And_Resolved.all; else Ind1 := File1.Value.Normalized_And_Resolved'First; Ind2 := File2.Value.Normalized_And_Resolved'First; for C in 1 .. File1.Value.Normalized_And_Resolved'Length loop if Ind2 > File2.Value.Normalized_And_Resolved'Last then return False; end if; C1 := To_Lower (File1.Value.Normalized_And_Resolved (Ind1)); C2 := To_Lower (File2.Value.Normalized_And_Resolved (Ind2)); if C1 < C2 then return True; elsif C1 > C2 then return False; end if; Ind1 := Ind1 + 1; Ind2 := Ind2 + 1; end loop; return True; end if; end if; end "<"; ------------ -- Create -- ------------ function Create (Full_Filename : Filesystem_String; Host : String := Local_Host; Normalize : Boolean := False) return Virtual_File is function Internal_Get_Path (FS : FS_Type) return FS_String; -- Get Full_Filename according to Normalize setting ----------------------- -- Internal_Get_Path -- ----------------------- function Internal_Get_Path (FS : FS_Type) return FS_String is begin if not Normalize then return +Full_Filename; end if; if GNATCOLL.Path.Is_Absolute_Path (FS, +Full_Filename) then return GNATCOLL.Path.Normalize (FS, +Full_Filename); else declare Full_Path : constant Virtual_File := Get_Current_Dir (Host => Host) / Full_Filename; begin return GNATCOLL.Path.Normalize (FS, +Full_Path.Full_Name); end; end if; end Internal_Get_Path; begin if Full_Filename = "" then return No_File; end if; if Host = Local_Host then return (Ada.Finalization.Controlled with Value => GNATCOLL.IO.Native.Create (Internal_Get_Path (GNATCOLL.Path.Local_FS))); else return (Ada.Finalization.Controlled with Value => GNATCOLL.IO.Remote.Create (Host, +Full_Filename, Normalize)); end if; end Create; ---------------------- -- Create_From_UTF8 -- ---------------------- function Create_From_UTF8 (Full_Filename : String; Host : String := Local_Host; Normalize : Boolean := False) return Virtual_File is begin if Host = Local_Host then return Create (+GNATCOLL.IO.Native.Codec.From_UTF8 (Full_Filename), Normalize => Normalize); else return Create (+GNATCOLL.IO.Remote.Codec.From_UTF8 (Full_Filename), Host, Normalize => Normalize); end if; end Create_From_UTF8; ---------------------- -- Create_From_Base -- ---------------------- function Create_From_Base (Base_Name : Filesystem_String; Base_Dir : Filesystem_String := ""; Host : String := Local_Host) return Virtual_File is FS : FS_Type; begin if Host = Local_Host then FS := Local_FS; else FS := Shell_FS (Get_Server (Host).all); end if; declare The_Name : constant FS_String := Path.From_Unix (FS, +Base_Name); The_Dir : constant FS_String := Path.From_Unix (FS, +Base_Dir); begin if Is_Absolute_Path (FS, The_Name) then return Create (+The_Name, Host); elsif The_Dir /= "" then return Create (+(Path.Ensure_Directory (FS, The_Dir) & The_Name), Host); else return Create_From_Dir (Get_Current_Dir (Host), +The_Name); end if; end; end Create_From_Base; --------------------- -- Create_From_Dir -- --------------------- function Create_From_Dir (Dir : Virtual_File; Base_Name : Filesystem_String; Normalize : Boolean := False) return Virtual_File is begin if Dir.Value = null then raise VFS_Invalid_File_Error; end if; Dir.Ensure_Directory; return (Ada.Finalization.Controlled with Dispatching_Create (Ref => Dir.Value, Full_Path => FS_String (Dir.Full_Name (Normalize).all) & From_Unix (Dir.Value.Get_FS, +Base_Name))); end Create_From_Dir; ------------------ -- Error_String -- ------------------ function Error_String (Self : Writable_File) return Ada.Strings.Unbounded.Unbounded_String is begin return Self.Error; end Error_String; -------------------- -- Locate_On_Path -- -------------------- function Locate_On_Path (Base_Name : Filesystem_String; Host : String := Local_Host) return Virtual_File is Name : GNAT.OS_Lib.String_Access; Ret : Virtual_File; use type GNAT.OS_Lib.String_Access; begin if Host = Local_Host then if Is_Absolute_Path (Local_FS, +Base_Name) then return Create (Base_Name); end if; Name := GNAT.OS_Lib.Locate_Exec_On_Path (+Base_Name); if Name = null then return No_File; else Ret := Create (+Name.all); GNAT.OS_Lib.Free (Name); return Ret; end if; else declare Int : constant GNATCOLL.IO.File_Access := GNATCOLL.IO.Remote.Locate_On_Path (Host, +Base_Name); begin if Int = null then return No_File; else return (Ada.Finalization.Controlled with Int); end if; end; end if; end Locate_On_Path; ----------------------- -- Get_Tmp_Directory -- ----------------------- function Get_Tmp_Directory (Host : String := Local_Host) return Virtual_File is begin if Host = Local_Host then return (Ada.Finalization.Controlled with GNATCOLL.IO.Native.Get_Tmp_Directory); else return (Ada.Finalization.Controlled with GNATCOLL.IO.Remote.Get_Tmp_Directory (Host)); end if; end Get_Tmp_Directory; ------------------------ -- Get_Home_Directory -- ------------------------ function Get_Home_Directory (Host : String := Local_Host) return Virtual_File is begin if Host = Local_Host then return (Ada.Finalization.Controlled with GNATCOLL.IO.Native.Home_Dir); else return (Ada.Finalization.Controlled with GNATCOLL.IO.Remote.Home_Dir (Host)); end if; end Get_Home_Directory; ------------------------ -- Get_Logical_Drives -- ------------------------ function Get_Logical_Drives (Host : String := Local_Host) return File_Array_Access is function Get_IO_List return GNATCOLL.IO.File_Array; -- Get the IO list depending on Host ----------------- -- Get_IO_List -- ----------------- function Get_IO_List return GNATCOLL.IO.File_Array is begin if Host = Local_Host then return GNATCOLL.IO.Native.Get_Logical_Drives; else return GNATCOLL.IO.Remote.Get_Logical_Drives (Host); end if; end Get_IO_List; List : constant GNATCOLL.IO.File_Array := Get_IO_List; Ret : constant File_Array_Access := new File_Array (1 .. List'Length); begin for J in List'Range loop Ret (J - List'First + 1) := (Ada.Finalization.Controlled with List (J)); Ret (J - List'First + 1).Value.Kind := Directory; end loop; return Ret; end Get_Logical_Drives; --------------------- -- Get_Current_Dir -- --------------------- function Get_Current_Dir (Host : String := Local_Host) return Virtual_File is Ret : Virtual_File; begin if Host = Local_Host then Ret := (Ada.Finalization.Controlled with GNATCOLL.IO.Native.Current_Dir); else Ret := (Ada.Finalization.Controlled with GNATCOLL.IO.Remote.Current_Dir (Host)); end if; Ret.Value.Kind := Directory; return Ret; end Get_Current_Dir; --------------- -- Base_Name -- --------------- function Base_Name (File : Virtual_File; Suffix : Filesystem_String := ""; Normalize : Boolean := False) return Filesystem_String is begin if File.Value = null then return ""; end if; if Normalize then Ensure_Normalized (File, Resolve_Symlinks => True); return +Base_Name (File.Value.Get_FS, File.Value.Normalized_And_Resolved.all, +Suffix); else return +Base_Name (File.Value.Get_FS, File.Value.Full.all, +Suffix); end if; end Base_Name; ------------------- -- Base_Dir_Name -- ------------------- function Base_Dir_Name (File : Virtual_File) return Filesystem_String is begin if File.Value = null then return ""; end if; return +Base_Dir_Name (File.Value.Get_FS, File.Value.Full.all); end Base_Dir_Name; --------------- -- Full_Name -- --------------- function Full_Name (File : Virtual_File; Normalize : Boolean := False) return Filesystem_String is begin return File.Full_Name (Normalize).all; end Full_Name; --------------- -- Full_Name -- --------------- function Full_Name (File : Virtual_File; Normalize : Boolean := False; Resolve_Links : Boolean := False) return Cst_Filesystem_String_Access is begin if File.Value = null then return Empty_String'Access; elsif File.Value.Full /= null and then Normalize then Ensure_Normalized (File, Resolve_Links); if Resolve_Links then return Cst_Filesystem_String_Access (+File.Value.Normalized_And_Resolved.all'Access); else return Cst_Filesystem_String_Access (+File.Value.Normalized.all'Access); end if; elsif File.Value.Full = null and then Get_Host (File) /= Local_Host then GNATCOLL.IO.Remote.Ensure_Initialized (GNATCOLL.IO.Remote.Remote_File_Access (File.Value)); return Cst_Filesystem_String_Access (+File.Value.Full.all'Access); else return Cst_Filesystem_String_Access (+File.Value.Full.all'Access); end if; end Full_Name; -------------------- -- Full_Name_Hash -- -------------------- function Full_Name_Hash (Key : Virtual_File) return Ada.Containers.Hash_Type is F : FS_Type; begin if Key.Value = null then return 0; end if; Ensure_Normalized (Key, Resolve_Symlinks => True); -- We also take care of potential trailing dir separator by enforcing -- them. The goal is that we have the same hash whenever "=" returns -- True, so that we can instantiate hash tables by using "=" for the -- equivalent key function F := Key.Value.Get_FS; if Is_Case_Sensitive (F) then return Ada.Strings.Hash (+Ensure_Directory (F, Key.Value.Normalized_And_Resolved.all)); else return Ada.Strings.Hash_Case_Insensitive (+Ensure_Directory (F, Key.Value.Normalized_And_Resolved.all)); end if; end Full_Name_Hash; -------------- -- Dir_Name -- -------------- function Dir_Name (File : Virtual_File) return Filesystem_String is begin if File.Value = null then return ""; end if; return +Dir_Name (File.Value.Get_FS, File.Value.Full.all); end Dir_Name; --------- -- Dir -- --------- function Dir (File : Virtual_File) return Virtual_File is begin if File.Value = null then return VFS.No_File; end if; if Is_Dir_Name (File.Value.Get_FS, File.Value.Full.all) then return File; else return (Ada.Finalization.Controlled with Value => Dispatching_Create (File.Value, Dir_Name (File.Value.Get_FS, File.Value.Full.all))); end if; end Dir; ----------------------- -- Display_Full_Name -- ----------------------- function Display_Full_Name (File : Virtual_File; Normalize : Boolean := False) return String is begin if File.Value = null then return ""; else return File.Value.To_UTF8 (FS_String (File.Full_Name (Normalize).all)); end if; end Display_Full_Name; ----------------------- -- Display_Base_Name -- ----------------------- function Display_Base_Name (File : Virtual_File; Suffix : Filesystem_String := "") return String is begin if File.Value = null then return ""; else return File.Value.To_UTF8 (+File.Base_Name (Suffix)); end if; end Display_Base_Name; --------------------- -- Locale_Dir_Name -- --------------------- function Display_Dir_Name (File : Virtual_File) return String is begin if File.Value = null then return ""; else return File.Value.To_UTF8 (+File.Dir_Name); end if; end Display_Dir_Name; --------------------------- -- Display_Base_Dir_Name -- --------------------------- function Display_Base_Dir_Name (File : Virtual_File) return String is begin if File.Value = null then return ""; else return File.Value.To_UTF8 (+File.Base_Dir_Name); end if; end Display_Base_Dir_Name; -------------------------- -- Unix_Style_Full_Name -- -------------------------- function Unix_Style_Full_Name (File : Virtual_File; Cygwin_Style : Boolean := False; Normalize : Boolean := False; Casing : Boolean := False) return Filesystem_String is FS : FS_Type; function Auto_Case (Str : FS_String) return Filesystem_String; function Auto_Case (Str : FS_String) return Filesystem_String is begin if not Casing or else Is_Case_Sensitive (FS) then return +Str; else return +To_Lower (+Str); end if; end Auto_Case; begin if File.Value = null then return ""; else FS := File.Value.Get_FS; if Normalize then return Auto_Case (To_Unix (FS, +Full_Name (File, Normalize => Normalize, Resolve_Links => True).all, Cygwin_Style)); else return Auto_Case (To_Unix (FS, File.Value.Full.all, Cygwin_Style)); end if; end if; end Unix_Style_Full_Name; ------------------- -- Relative_Path -- ------------------- function Relative_Path (File : Virtual_File; From : Virtual_File) return Filesystem_String is begin -- Obviously, we need from to be a directory... if not Is_Directory (From) then return File.Full_Name; end if; if From.Value = null or else File.Value = null or else File.Value'Tag /= From.Value'Tag then return File.Full_Name; end if; return Filesystem_String (Relative_Path (File.Value.Get_FS, Ref => FS_String (From.Full_Name.all), Path => FS_String (File.Full_Name.all))); end Relative_Path; ---------------- -- Has_Suffix -- ---------------- function Has_Suffix (File : Virtual_File; Suffix : Filesystem_String) return Boolean is begin return File.Value /= null and then File.Full_Name.all'Length >= Suffix'Length and then Equal (File.Value.Get_FS, File.Value.Full (File.Value.Full'Last - Suffix'Length + 1 .. File.Value.Full'Last), +Suffix); end Has_Suffix; --------------- -- To_Remote -- --------------- function To_Remote (File : Virtual_File; To_Host : String) return Virtual_File is begin if File.Value = null then return No_File; end if; if File.Get_Host = To_Host then return File; end if; if not Is_Configured (To_Host) then raise VFS_Remote_Config_Error; end if; declare Server : constant Server_Access := Get_Server (To_Host); Local : Virtual_File; Remote : Virtual_File := No_File; begin for J in 1 .. Nb_Mount_Points (Server.Nickname) loop Local := Create (+Get_Mount_Point_Local_Root (Server.Nickname, J)); if Local.Is_Parent (File) then Remote := Create (+Get_Mount_Point_Host_Root (Server.Nickname, J), To_Host); exit; end if; end loop; if Remote = No_File then -- Simple conversion return Convert (File, To_Host); else return Convert (File, Local, Remote); end if; end; end To_Remote; -------------- -- To_Local -- -------------- function To_Local (File : Virtual_File) return Virtual_File is begin if File.Value = null then return No_File; end if; if File.Is_Local then return File; end if; if not Is_Configured (File.Get_Host) then raise VFS_Remote_Config_Error; end if; declare Server : constant Server_Access := Get_Server (File.Get_Host); Local : Virtual_File := No_File; Remote : Virtual_File; begin for J in 1 .. Nb_Mount_Points (Server.Nickname) loop Remote := Create (+Get_Mount_Point_Host_Root (Server.Nickname, J), File.Get_Host); if Remote.Is_Parent (File) then Local := Create (+Get_Mount_Point_Local_Root (Server.Nickname, J)); exit; end if; end loop; if Local = No_File then -- Simple conversion return Convert (File, Local_Host); else return Convert (File, Remote, Local); end if; end; end To_Local; ------------ -- To_Arg -- ------------ function To_Arg (File : Virtual_File; Host : String := Local_Host) return GNAT.Strings.String_Access is Host_File : Virtual_File; begin if Host /= File.Get_Host then if File.Get_Host /= Local_Host then Host_File := File.To_Local; else Host_File := File; end if; if Host /= Local_Host then Host_File := Host_File.To_Remote (Host); end if; return new String'(String (Host_File.Full_Name.all)); end if; return new String'(String (File.Full_Name.all)); end To_Arg; ------------- -- Convert -- ------------- function Convert (File : Virtual_File; To_Host : String) return Virtual_File is begin if File.Value = null then return No_File; end if; -- Create always handles both paths in Unix form, or paths in Host's -- form. So we first translate the path to unix (possible whatever the -- current path format is), then it's up to Create to correctly format -- the path. return Create (+To_Unix (File.Value.Get_FS, FS_String (File.Full_Name.all)), To_Host); end Convert; ------------- -- Convert -- ------------- function Convert (File : Virtual_File; From_Dir : Virtual_File; To_Dir : Virtual_File) return Virtual_File is begin if File.Value = null or else From_Dir.Value = null or else To_Dir.Value = null then return No_File; end if; if From_Dir.Value.Get_FS /= To_Dir.Value.Get_FS then return Create_From_Dir (To_Dir, +To_Unix (File.Value.Get_FS, +File.Relative_Path (From_Dir))); else return Create_From_Dir (To_Dir, File.Relative_Path (From_Dir)); end if; end Convert; -------------------- -- Unchecked_Free -- -------------------- procedure Unchecked_Free (Arr : in out File_Array_Access) is procedure Internal is new Ada.Unchecked_Deallocation (File_Array, File_Array_Access); begin Internal (Arr); end Unchecked_Free; -------------- -- Is_Local -- -------------- function Is_Local (File : Virtual_File) return Boolean is begin return File.Value = null or else File.Value.all in GNATCOLL.IO.Native.Native_File_Record'Class; end Is_Local; ---------- -- Host -- ---------- function Get_Host (File : Virtual_File) return String is begin if Is_Local (File) then return Local_Host; else return GNATCOLL.IO.Remote.Get_Host (Remote_File_Record (File.Value.all)'Access); end if; end Get_Host; --------------------- -- Is_Regular_File -- --------------------- function Is_Regular_File (File : Virtual_File) return Boolean is Ret : Boolean; begin if File.Value = null then return False; elsif File.Value.all not in GNATCOLL.IO.Native.Native_File_Record'Class and then File.Value.Kind /= Unknown -- Only use cache for remote files then return File.Value.Kind = GNATCOLL.IO.File; else Ret := File.Value.Is_Regular_File; if Ret then File.Value.Kind := GNATCOLL.IO.File; end if; return Ret; end if; end Is_Regular_File; ---------- -- Size -- ---------- function Size (File : Virtual_File) return Long_Integer is begin if File.Value = null then return 0; else return File.Value.Size; end if; end Size; ------------ -- Rename -- ------------ procedure Rename (File : Virtual_File; Full_Name : Virtual_File; Success : out Boolean) is begin if File.Value = null or else Full_Name.Value = null or else File.Value'Tag /= Full_Name.Value'Tag then Success := False; else Rename (File.Value, Full_Name.Value, Success); if Success then Full_Name.Value.Kind := File.Value.Kind; File.Value.Kind := Unknown; end if; end if; end Rename; ---------- -- Copy -- ---------- procedure Copy (File : Virtual_File; Target_Name : Filesystem_String; Success : out Boolean) is begin if File.Value = null then Success := False; end if; if Is_Directory (File) then File.Value.Copy_Dir (+Target_Name, Success); else File.Value.Copy (+Target_Name, Success); end if; end Copy; ------------ -- Delete -- ------------ procedure Delete (File : Virtual_File; Success : out Boolean) is begin if File.Value = null then Success := False; else File.Value.Delete (Success); end if; end Delete; ----------------- -- Is_Readable -- ----------------- function Is_Readable (File : Virtual_File) return Boolean is begin return File.Value /= null and then File.Value.Is_Readable; end Is_Readable; ----------------- -- Is_Writable -- ----------------- function Is_Writable (File : Virtual_File) return Boolean is begin return File.Value /= null and then File.Value.Is_Writable; end Is_Writable; ------------------ -- Is_Directory -- ------------------ function Is_Directory (VF : Virtual_File) return Boolean is Ret : Boolean; begin if VF.Value = null then return False; elsif VF.Value.all not in GNATCOLL.IO.Native.Native_File_Record'Class and then VF.Value.Kind /= Unknown -- Only use cache for remote files then return VF.Value.Kind = Directory; else Ret := VF.Value.Is_Directory; if Ret then VF.Ensure_Directory; VF.Value.Kind := Directory; end if; return Ret; end if; end Is_Directory; ---------------------- -- Is_Symbolic_Link -- ---------------------- function Is_Symbolic_Link (File : Virtual_File) return Boolean is begin return File.Value /= null and then File.Value.Is_Symbolic_Link; end Is_Symbolic_Link; ---------------------- -- Is_Absolute_Path -- ---------------------- function Is_Absolute_Path (File : Virtual_File) return Boolean is begin return File.Value /= null and then Is_Absolute_Path (File.Value.Get_FS, File.Value.Full.all); end Is_Absolute_Path; -------------------- -- File_Extension -- -------------------- function File_Extension (File : Virtual_File; Normalize : Boolean := False) return Filesystem_String is begin if File.Value = null then return ""; else return Filesystem_String (File_Extension (File.Value.Get_FS, FS_String (File.Full_Name (Normalize).all))); end if; end File_Extension; --------------- -- Read_File -- --------------- function Read_File (File : Virtual_File) return GNAT.Strings.String_Access is begin if File.Value = null then return null; elsif File.Value.Kind = Directory then return null; else return File.Value.Read_Whole_File; end if; end Read_File; --------------- -- Read_File -- --------------- function Read_File (File : Virtual_File) return GNATCOLL.Strings.XString is begin if File.Value = null or else File.Value.Kind = Directory then return GNATCOLL.Strings.Null_XString; else return File.Value.Read_Whole_File; end if; end Read_File; ---------------- -- Write_File -- ---------------- function Write_File (File : Virtual_File; Append : Boolean := False) return Writable_File is use type GNAT.OS_Lib.File_Descriptor; W : Writable_File; function Temporary_File (From : Virtual_File) return Virtual_File; -- Return a temporary file on the host as From. -- Return No_File if we couldn't create one. -------------------- -- Temporary_File -- -------------------- function Temporary_File (From : Virtual_File) return Virtual_File is use GNAT.OS_Lib; subtype Lowercase is Character range 'a' .. 'z'; package Random_Char is new Ada.Numerics.Discrete_Random (Lowercase); Gen : Random_Char.Generator; Tmp_Dir : constant Virtual_File := Get_Tmp_Directory (From.Get_Host); Pid_Int : constant Integer := Pid_To_Integer (Current_Process_Id); Pid : constant Filesystem_String := +Integer'Image (Pid_Int); begin Random_Char.Reset (Gen); -- Make 10 attempts to find a random name that isn't already taken for J in 1 .. 10 loop declare R : constant Filesystem_String (1 .. 8) := (others => Random_Char.Random (Gen)); Temp : Virtual_File; begin -- Bake in "vfs" and the PID in the temporary file name, -- potentially useful for development and debugging. Temp := Create_From_Dir (Tmp_Dir, "vfs-" & Pid (Pid'First + 1 .. Pid'Last) & "-" & R); if not Temp.Is_Regular_File then -- A file with that name doesn't already exist? use it. return Temp; end if; end; end loop; -- ... If all these files existed, return an error return No_File; end Temporary_File; begin if File.Value = null then return Invalid_File; end if; W.File := File; W.Append := Append; W.Success := True; if not Append or else not File.Is_Regular_File then -- Not appending, or appending to a file that doesn't exist -- yet: write to a temporary file first. W.Tmp_File := Temporary_File (File); -- Check whether we could actually create the temporary file if W.Tmp_File = No_File then return X : Writable_File := Invalid_File do X.Error := Ada.Strings.Unbounded.To_Unbounded_String ("Could not create a temporary file"); end return; end if; W.Tmp_File.Value.Open_Write (Append => False, FD => W.FD, Error => W.Error); else W.Tmp_File := No_File; -- append-mode, and the file already exists. File.Value.Open_Write (Append => True, FD => W.FD, Error => W.Error); end if; if W.FD = GNAT.OS_Lib.Invalid_FD then return X : Writable_File := Invalid_File do X.Error := W.Error; end return; else return W; end if; end Write_File; ----------- -- Write -- ----------- procedure Write (File : in out Writable_File; Str : String) is Written : aliased Integer; begin if File.Success then Written := GNAT.OS_Lib.Write (File.FD, Str'Address, Str'Length); File.Success := Written = Str'Length; if not File.Success then File.Error := Ada.Strings.Unbounded.To_Unbounded_String ("Disk full"); end if; if Written > 0 then -- File has been overwritten on the disk anyway if File.Tmp_File /= No_File then File.Tmp_File.Value.Kind := GNATCOLL.IO.File; else File.File.Value.Kind := GNATCOLL.IO.File; end if; end if; end if; end Write; ----------- -- Write -- ----------- procedure Write (File : in out Writable_File; Str : chars_ptr) is Written : aliased Integer; Len : Integer; function To_Address is new Ada.Unchecked_Conversion (chars_ptr, System.Address); begin if File.Success then Len := Integer (Strlen (Str)); Written := GNAT.OS_Lib.Write (File.FD, To_Address (Str), Len); File.Success := Written = Len; if not File.Success then File.Error := Ada.Strings.Unbounded.To_Unbounded_String ("Disk full"); end if; if Written > 0 then File.File.Value.Kind := GNATCOLL.IO.File; end if; end if; end Write; ----------- -- Close -- ----------- procedure Close (File : in out Writable_File) is use Ada.Strings.Unbounded; Norm : Virtual_File; Success : Boolean; -- We'll need to force the resolution of symbolic links, -- since we never want to transform a link into a regular -- file (which among other things breaks support for CM Synergy) Save_Handle_Symbolic_Links : constant Boolean := Handle_Symbolic_Links; begin if File.Success then if File.Tmp_File /= No_File then File.Tmp_File.Value.Close (File.FD, File.Success); if not File.Success then File.Error := To_Unbounded_String ("close() failed"); else -- Look past symbolic links. We do not want to impact the -- normalized name saved in File, so we need to use local -- copies. Handle_Symbolic_Links := True; Norm := Create (File.File.Full_Name.all); Norm := Create (Norm.Full_Name (Normalize => True, Resolve_Links => True).all); Handle_Symbolic_Links := Save_Handle_Symbolic_Links; -- Preserve permissions on the file Copy_File_Permissions (From => Norm.Value, To => File.Tmp_File.Value, Success => Success); -- ??? We ignore the value of success here. It is not a -- critical error, however, and we want to continue the -- process of saving here. It would be nice to log this, -- but we cannot use Traces in this package. -- We use to delete explicitly Norm. But in fact this is not a -- good idea, since the directory might allow us to delete the -- file (or not), but not to recreate it afterwards, as seem to -- be the case with CM Synergy. File.Tmp_File.Rename (Norm, File.Success); if not File.Success then -- Renaming failed. It might be because it could not -- remove the original (read-only directory for instance) -- so let's try with a simple copy instead File.Tmp_File.Copy (Norm.Full_Name (Normalize => True).all, File.Success); if File.Success then File.Tmp_File.Delete (Success); -- ignore Success, that's fine if the temp file is -- still there. else File.Error := To_Unbounded_String (+("Copy failed from " & File.Tmp_File.Full_Name.all & " to " & Norm.Full_Name.all)); end if; end if; end if; else File.File.Value.Close (File.FD, File.Success); end if; end if; if not File.Success then raise Ada.Text_IO.Use_Error with "Error while writing to the file" & (if File.Error /= Null_Unbounded_String then " (" & To_String (File.Error) & ")" else ""); end if; end Close; ------------------ -- Set_Writable -- ------------------ procedure Set_Writable (File : VFS.Virtual_File; Writable : Boolean) is begin if File.Value = null then raise VFS_Invalid_File_Error; end if; File.Value.Set_Writable (Writable); end Set_Writable; ------------------ -- Set_Readable -- ------------------ procedure Set_Readable (File : VFS.Virtual_File; Readable : Boolean) is begin if File.Value = null then raise VFS_Invalid_File_Error; end if; File.Value.Set_Readable (Readable); end Set_Readable; --------------------- -- File_Time_Stamp -- --------------------- function File_Time_Stamp (File : Virtual_File) return Ada.Calendar.Time is begin if File.Value = null then return GNATCOLL.Utils.No_Time; end if; return File.Value.File_Time_Stamp; end File_Time_Stamp; ---------------------- -- Ensure_Directory -- ---------------------- procedure Ensure_Directory (Dir : Virtual_File) is Full : FS_String_Access; begin if Dir.Value /= null then if not Is_Dir_Name (Dir.Value.Get_FS, Dir.Value.Full.all) then Full := new FS_String' (Ensure_Directory (Dir.Value.Get_FS, Dir.Value.Full.all)); Free (Dir.Value.Full); Dir.Value.Full := Full; end if; if Dir.Value.Normalized /= null and then not Is_Dir_Name (Dir.Value.Get_FS, Dir.Value.Normalized.all) then Full := new FS_String' (Ensure_Directory (Dir.Value.Get_FS, Dir.Value.Normalized.all)); if Dir.Value.Normalized /= Dir.Value.Normalized_And_Resolved then Free (Dir.Value.Normalized_And_Resolved); end if; Free (Dir.Value.Normalized); Dir.Value.Normalized_And_Resolved := null; Dir.Value.Normalized := Full; end if; end if; end Ensure_Directory; ----------------------- -- Ensure_Normalized -- ----------------------- procedure Ensure_Normalized (File : Virtual_File'Class; Resolve_Symlinks : Boolean) is begin if File.Value = null then return; end if; if File.Value.Normalized = null then File.Value.Normalized := new FS_String' (Path.Normalize (File.Value.Get_FS, File.Value.Full.all)); end if; if Resolve_Symlinks then if Handle_Symbolic_Links then GNATCOLL.IO.Resolve_Symlinks (File.Value); else if File.Value.Normalized_And_Resolved = null then File.Value.Normalized_And_Resolved := File.Value.Normalized; end if; end if; end if; end Ensure_Normalized; -------------------- -- Normalize_Path -- -------------------- procedure Normalize_Path (File : Virtual_File; Resolve_Symlinks : Boolean := False) is begin if File.Value = null then return; end if; Ensure_Normalized (File, Resolve_Symlinks); Free (File.Value.Full); if Resolve_Symlinks then File.Value.Full := new FS_String'(File.Value.Normalized_And_Resolved.all); else File.Value.Full := new FS_String'(File.Value.Normalized.all); end if; end Normalize_Path; -------------- -- Get_Root -- -------------- function Get_Root (File : Virtual_File) return Virtual_File is begin if File.Value = null then return No_File; end if; return (Ada.Finalization.Controlled with Dispatching_Create (File.Value, Get_Root (File.Value.Get_FS, File.Value.Full.all))); end Get_Root; ---------------- -- Get_Parent -- ---------------- function Get_Parent (Dir : Virtual_File) return Virtual_File is begin if Dir.Value = null then return No_File; end if; declare Parent : constant FS_String := Get_Parent (Dir.Value.Get_FS, Dir.Value.Full.all); begin if Parent = "" then return No_File; else return (Ada.Finalization.Controlled with Dispatching_Create (Dir.Value, Parent)); end if; end; end Get_Parent; ------------- -- Sub_Dir -- ------------- function Sub_Dir (Dir : Virtual_File; Name : Filesystem_String) return Virtual_File is New_Dir : Virtual_File; begin Ensure_Directory (Dir); New_Dir := (Ada.Finalization.Controlled with Dispatching_Create (Dir.Value, GNATCOLL.Path.Path (Dir.Value.Get_FS, "", FS_String (Dir.Full_Name.all), FS_String (Name)))); Ensure_Directory (New_Dir); if Is_Directory (New_Dir) and then True then return New_Dir; else return No_File; end if; end Sub_Dir; ---------------- -- Change_Dir -- ---------------- procedure Change_Dir (Dir : Virtual_File) is Success : Boolean; pragma Unreferenced (Success); begin if Dir.Value = null then Raise_Exception (VFS_Directory_Error'Identity, "Dir is No_File"); end if; Success := Dir.Value.Change_Dir; end Change_Dir; -------------- -- Make_Dir -- -------------- procedure Make_Dir (Dir : Virtual_File; Recursive : Boolean := True) is Result : Boolean; begin if Dir.Value = null then Raise_Exception (VFS_Directory_Error'Identity, "Dir is No_File"); end if; -- If Dir already exists and is a directory, then return if Is_Directory (Dir) then return; end if; Result := Dir.Value.Make_Dir (Recursive); if not Result then Dir.Value.Kind := Unknown; Raise_Exception (VFS_Directory_Error'Identity, "Dir cannot be created"); else Dir.Value.Kind := Directory; end if; exception when E : others => Raise_Exception (VFS_Directory_Error'Identity, Exception_Message (E)); end Make_Dir; ---------------- -- Remove_Dir -- ---------------- procedure Remove_Dir (Dir : Virtual_File; Recursive : Boolean := False; Success : out Boolean) is begin if Dir.Value = null then raise VFS_Directory_Error; end if; Dir.Value.Remove_Dir (Recursive, Success); if Success then Dir.Value.Kind := Unknown; end if; end Remove_Dir; -------------- -- Read_Dir -- -------------- function Read_Dir (Dir : Virtual_File; Filter : Read_Dir_Filter := All_Files) return File_Array_Access is F_Array : File_Array_Access; Tmp_File : Virtual_File; begin if Dir.Value = null then Raise_Exception (VFS_Directory_Error'Identity, "Dir is No_File"); end if; Ensure_Directory (Dir); if not Is_Directory (Dir) then Raise_Exception (VFS_Directory_Error'Identity, "Dir is not a directory"); end if; declare List : GNAT.Strings.String_List := Dir.Value.Read_Dir (Dirs_Only => Filter = Dirs_Only, Files_Only => Filter = Files_Only); begin F_Array := new File_Array (1 .. List'Length); for J in List'Range loop Tmp_File := Dir.Create_From_Dir (+List (J).all); case Filter is when Dirs_Only => Tmp_File.Value.Kind := Directory; when Files_Only => Tmp_File.Value.Kind := File; when others => null; end case; F_Array (F_Array'First + J - List'First) := Tmp_File; GNAT.Strings.Free (List (J)); end loop; end; return F_Array; exception when E : others => Unchecked_Free (F_Array); Raise_Exception (VFS_Directory_Error'Identity, Exception_Message (E)); end Read_Dir; ------------------------ -- Read_Dir_Recursive -- ------------------------ function Read_Dir_Recursive (Dir : Virtual_File; Extension : Filesystem_String := ""; Filter : Read_Dir_Filter := All_Files) return File_Array_Access is Result : File_Array_Access; procedure Internal (Directory : Virtual_File); -- process a directory recursively procedure Internal (Directory : Virtual_File) is Files : File_Array_Access := Directory.Read_Dir (Filter => All_Files); begin if Files = null then return; end if; for F in Files'Range loop declare B : constant Filesystem_String := Files (F).Base_Name; begin if B /= "." and then B /= ".." then if Extension = "" or else Files (F).File_Extension = Extension then case Filter is when Dirs_Only => if Files (F).Is_Directory then Append (Result, Files (F)); end if; when Files_Only => if Files (F).Is_Regular_File then Append (Result, Files (F)); end if; when All_Files => Append (Result, Files (F)); end case; end if; if Files (F).Is_Directory then Internal (Files (F)); end if; end if; end; end loop; Unchecked_Free (Files); end Internal; begin if Dir.Is_Directory then Internal (Dir); end if; return Result; end Read_Dir_Recursive; -------------------------- -- Read_Files_From_Dirs -- -------------------------- function Read_Files_From_Dirs (Dirs : File_Array) return File_Array_Access is Ret : File_Array_Access := null; Files : array (Dirs'Range) of File_Array_Access; Length : Natural := 0; Idx : Natural; begin for J in Dirs'Range loop begin Files (J) := Read_Dir (Dirs (J), Files_Only); if Files (J) /= null then Length := Length + Files (J)'Length; end if; exception when VFS_Directory_Error => Files (J) := null; end; end loop; if Length = 0 then return null; else Ret := new File_Array (1 .. Length); Idx := Ret'First; for J in Files'Range loop if Files (J) /= null then Ret (Idx .. Idx + Files (J)'Length - 1) := Files (J).all; Idx := Idx + Files (J)'Length; Unchecked_Free (Files (J)); end if; end loop; return Ret; end if; end Read_Files_From_Dirs; -------------- -- Open_Dir -- -------------- function Open_Dir (Dir : Virtual_File) return Virtual_Dir is VDir : Virtual_Dir; begin if Dir.Value = null then return Invalid_Dir; end if; VDir.File := Dir; VDir.Files_List := Read_Dir (Dir); if VDir.Files_List /= null then VDir.Current := VDir.Files_List'First - 1; end if; Dir.Value.Kind := Directory; return VDir; exception when VFS_Directory_Error => return Invalid_Dir; end Open_Dir; ---------- -- Read -- ---------- procedure Read (VDir : in out Virtual_Dir; File : out Virtual_File) is begin if VDir.Files_List /= null and then VDir.Current < VDir.Files_List'Last then VDir.Current := VDir.Current + 1; File := VDir.Files_List (VDir.Current); else File := No_File; end if; end Read; ----------- -- Close -- ----------- procedure Close (VDir : in out Virtual_Dir) is begin if VDir.Files_List /= null then Unchecked_Free (VDir.Files_List); end if; VDir := Invalid_Dir; end Close; -------------- -- Finalize -- -------------- overriding procedure Finalize (File : in out Virtual_File) is Value : GNATCOLL.IO.File_Access := File.Value; begin File.Value := null; -- Make Finalize idempotent if Value /= null then Unref (Value); end if; end Finalize; ------------ -- Adjust -- ------------ overriding procedure Adjust (File : in out Virtual_File) is begin if File.Value /= null then Ref (File.Value); end if; end Adjust; --------------- -- Is_Parent -- --------------- function Is_Parent (Parent, Child : Virtual_File) return Boolean is begin if Parent.Value = null or else Child.Value = null or else Parent.Value'Tag /= Child.Value'Tag then return False; end if; Ensure_Normalized (Parent, Resolve_Symlinks => True); Ensure_Normalized (Child, Resolve_Symlinks => True); if Parent.Value.Normalized_And_Resolved'Length > Child.Value.Normalized_And_Resolved'Length then return False; end if; return Equal (Parent.Value.Get_FS, Parent.Value.Normalized_And_Resolved.all, Child.Value.Normalized_And_Resolved (Child.Value.Normalized_And_Resolved'First .. Child.Value.Normalized_And_Resolved'First + Parent.Value.Normalized_And_Resolved'Length - 1)); end Is_Parent; ---------- -- Sort -- ---------- procedure Sort (Files : in out File_Array) is -- ??? Right now, this sorts only on the full name. Do we want to -- provide other choices for sorting ? procedure Xchg (Op1, Op2 : Natural); -- Exchanges two items in the array function Lt (Op1, Op2 : Natural) return Boolean; -- Return True if the first item is to be sorted before the second ---------- -- Xchg -- ---------- procedure Xchg (Op1, Op2 : Natural) is Buffer : Virtual_File; begin Buffer := Files (Files'First - 1 + Op1); Files (Files'First - 1 + Op1) := Files (Files'First - 1 + Op2); Files (Files'First - 1 + Op2) := Buffer; end Xchg; -------- -- Lt -- -------- function Lt (Op1, Op2 : Natural) return Boolean is begin return Files (Files'First - 1 + Op1) < Files (Files'First - 1 + Op2); end Lt; begin Sort (Files'Length, Xchg'Unrestricted_Access, Lt'Unrestricted_Access); end Sort; ------------ -- Append -- ------------ procedure Append (Files : in out File_Array_Access; F : Virtual_File) is begin Append (Files, File_Array'(1 => F)); end Append; ------------ -- Append -- ------------ procedure Append (Files : in out File_Array_Access; F : File_Array) is Tmp : File_Array_Access; begin if Files = null then Files := new File_Array'(F); else Tmp := new File_Array (1 .. Files'Length + F'Length); Tmp (1 .. Files'Length) := Files.all; Tmp (Files'Length + 1 .. Tmp'Last) := F; Unchecked_Free (Files); Files := Tmp; end if; end Append; ------------- -- Prepend -- ------------- procedure Prepend (Files : in out File_Array_Access; F : File_Array) is Tmp : File_Array_Access; begin if Files = null then Files := new File_Array'(F); else Tmp := new File_Array (1 .. Files'Length + F'Length); Tmp (1 + F'Length .. Tmp'Length) := Files.all; Tmp (1 .. F'Length) := F; Unchecked_Free (Files); Files := Tmp; end if; end Prepend; ------------ -- Remove -- ------------ procedure Remove (Files : in out File_Array_Access; F : Virtual_File) is Tmp : File_Array_Access; begin for J in Files'Range loop if Files (J) = F then for K in J + 1 .. Files'Last loop Files (K - 1) := Files (K); end loop; Tmp := new File_Array'(Files (Files'First .. Files'Last - 1)); Unchecked_Free (Files); Files := Tmp; return; end if; end loop; end Remove; ------------- -- To_Path -- ------------- function To_Path (Paths : File_Array) return Filesystem_String is Length : Natural := 0; begin if Paths'Length = 0 then return ""; end if; for J in Paths'Range loop Length := Length + Paths (J).Full_Name.all'Length; end loop; Length := Length + Paths'Length - 1; declare Ret : Filesystem_String (1 .. Length); Idx : Natural := Ret'First; begin for J in Paths'Range loop Ret (Idx .. Idx + Paths (J).Full_Name.all'Length - 1) := Paths (J).Full_Name.all; Idx := Idx + Paths (J).Full_Name.all'Length; if J /= Paths'Last then Ret (Idx) := GNAT.OS_Lib.Path_Separator; Idx := Idx + 1; end if; end loop; return Ret; end; end To_Path; --------------- -- From_Path -- --------------- function From_Path (Path : Filesystem_String) return File_Array is Ret : File_Array_Access; Last : Natural := Path'First; begin for J in Path'First .. Path'Last loop -- ??? Should define Path_Separator in FS (system-dependent) if Path (J) = GNAT.OS_Lib.Path_Separator then if Last < J then Append (Ret, Create (Path (Last .. J - 1))); end if; Last := J + 1; end if; end loop; if Last <= Path'Last then Append (Ret, Create (Path (Last .. Path'Last))); end if; if Ret = null then return (1 .. 0 => <>); end if; declare Final : constant File_Array := Ret.all; begin Unchecked_Free (Ret); return Final; end; end From_Path; -------------------- -- Locate_On_Path -- -------------------- function Locate_On_Path (Base_Name : Filesystem_String; Path : File_Array) return Virtual_File is File : Virtual_File; begin for J in Path'Range loop if Path (J) /= No_File then File := Create_From_Dir (Path (J), Base_Name); if Is_Regular_File (File) then return File; end if; File := Create_From_Dir (Path (J), Base_Name & (+Exe_Extension (Path (J).Value.Get_FS))); if Is_Regular_File (File) then return File; end if; end if; end loop; return No_File; end Locate_On_Path; -------------------------- -- Greatest_Common_Path -- -------------------------- function Greatest_Common_Path (L : GNATCOLL.VFS.File_Array) return Virtual_File is begin if L'Length = 0 then return GNATCOLL.VFS.No_File; end if; declare Greatest_Prefix : Virtual_File := L (L'First); Root : constant Virtual_File := Get_Root (Greatest_Prefix); begin for J in L'First + 1 .. L'Last loop -- Loop until GP is a parent of the current File while not Greatest_Prefix.Is_Parent (L (J)) loop -- If not a parent, and already at root, then there is no -- greatest prefix. if Greatest_Prefix = Root then return No_File; end if; Greatest_Prefix := Get_Parent (Greatest_Prefix); end loop; end loop; return Greatest_Prefix; end; end Greatest_Common_Path; ------------------------- -- Locate_Regular_File -- ------------------------- function Locate_Regular_File (File_Name : Filesystem_String; Path : File_Array) return Virtual_File is F : Virtual_File; begin for J in Path'Range loop F := Create_From_Dir (Path (J), File_Name); if F.Is_Regular_File then return F; end if; end loop; return No_File; end Locate_Regular_File; ---------------------------- -- Symbolic_Links_Support -- ---------------------------- procedure Symbolic_Links_Support (Active : Boolean) is begin Handle_Symbolic_Links := Active; end Symbolic_Links_Support; ---------- -- Join -- ---------- function Join (Self : Virtual_File; File : Virtual_File) return Virtual_File is begin return Create_From_Dir (Self, File.Full_Name.all); end Join; ---------- -- Join -- ---------- function Join (Self : Virtual_File; Path : Filesystem_String) return Virtual_File is begin return Create_From_Dir (Self, Path); end Join; --------- -- "/" -- --------- function "/" (Self : Virtual_File; File : Virtual_File) return Virtual_File is begin return Create_From_Dir (Self, File.Full_Name.all); end "/"; --------- -- "/" -- --------- function "/" (Self : Virtual_File; Path : Filesystem_String) return Virtual_File is begin return Create_From_Dir (Self, Path); end "/"; --------- -- "/" -- --------- function "/" (Dir : Filesystem_String; File : Virtual_File) return Virtual_File is begin return Create_From_Dir (Create (Dir), File.Full_Name.all); end "/"; end GNATCOLL.VFS; gnatcoll-core-21.0.0/src/gnatcoll-scripts-impl.adb0000644000175000017500000002445113661715457021700 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Traces; use GNATCOLL.Traces; with System.Assertions; package body GNATCOLL.Scripts.Impl is procedure Console_Command_Handler (Data : in out Callback_Data'Class; Command : String); -- Handles command related to Console class procedure Logger_Handler (Data : in out Callback_Data'Class; Command : String); -- Handles command related to Logger class type Logger_Properties_Record is new Instance_Property_Record with record Handle : Trace_Handle; end record; type Logger_Properties is access all Logger_Properties_Record'Class; ----------------- -- Insert_Text -- ----------------- procedure Insert_Text (Script : access Scripting_Language_Record'Class; Console : Virtual_Console := null; Txt : String) is begin if (Console /= null and then Console.Hide_Output) or else (Script.Console /= null and then Script.Console.Hide_Output) then null; elsif Console /= null then Insert_Text (Console, Txt); elsif Script.Console /= null then Insert_Text (Script.Console, Txt); end if; end Insert_Text; ------------------ -- Insert_Error -- ------------------ procedure Insert_Error (Script : access Scripting_Language_Record'Class; Console : Virtual_Console := null; Txt : String) is begin if Console /= null then Insert_Error (Console, Txt); elsif Script.Console /= null then Insert_Error (Script.Console, Txt); end if; end Insert_Error; ------------------- -- Insert_Prompt -- ------------------- procedure Insert_Prompt (Script : access Scripting_Language_Record'Class; Console : Virtual_Console := null; Txt : String) is begin if Console /= null then Insert_Prompt (Console, Txt); elsif Script.Console /= null then Insert_Prompt (Script.Console, Txt); end if; end Insert_Prompt; ----------------------------- -- Console_Command_Handler -- ----------------------------- procedure Console_Command_Handler (Data : in out Callback_Data'Class; Command : String) is type Mode_Kinds is (Text, Log, Error); Inst : constant Class_Instance := Nth_Arg (Data, 1, Any_Class); Console : Virtual_Console; Mode : Mode_Kinds := Text; begin if Command = "write" then if Number_Of_Arguments (Data) = 3 then begin Mode := Mode_Kinds'Value (Nth_Arg (Data, 3)); exception when Constraint_Error => Set_Error_Msg (Data, "Wrong value for ""mode"" parameter"); return; end; end if; Console := Get_Data (Inst); if Console /= null then case Mode is when Text => Insert_Text (Console, Nth_Arg (Data, 2)); when Log => Insert_Log (Console, Nth_Arg (Data, 2)); when Error => Insert_Error (Console, Nth_Arg (Data, 2)); end case; else Set_Error_Msg (Data, "Console was closed by user"); end if; elsif Command = "clear" then Console := Get_Data (Inst); if Console /= null then Clear (Console); else Set_Error_Msg (Data, "Console was closed by user"); end if; elsif Command = "flush" then null; -- Do nothing, only needed for compatibility with Python's -- stdout stream elsif Command = "isatty" then Set_Return_Value (Data, False); elsif Command = "read" then Console := Get_Data (Inst); if Console /= null then Set_Return_Value (Data, Read (Console, Size => Nth_Arg (Data, 2, Integer'Last), Whole_Line => False)); else Set_Error_Msg (Data, "Console was closed by user"); end if; elsif Command = "readline" then Console := Get_Data (Inst); if Console /= null then Set_Return_Value (Data, Read (Console, Size => Nth_Arg (Data, 2, Integer'Last), Whole_Line => True)); else Set_Error_Msg (Data, "Console was closed by user"); end if; end if; end Console_Command_Handler; -------------------- -- Logger_Handler -- -------------------- procedure Logger_Handler (Data : in out Callback_Data'Class; Command : String) is Logger_Data : constant String := "Logger"; Inst : constant Class_Instance := Nth_Arg (Data, 1); Prop : Instance_Property; begin if Command = Constructor_Method then Set_Data (Inst, Logger_Data, Logger_Properties_Record' (Handle => Create (Nth_Arg (Data, 2)))); elsif Command = "log" then Prop := Get_Data (Inst, Logger_Data); Trace (Logger_Properties (Prop).Handle, Nth_Arg (Data, 2)); elsif Command = "set_active" then Prop := Get_Data (Inst, Logger_Data); Set_Active (Logger_Properties (Prop).Handle, Nth_Arg (Data, 2)); elsif Command = "active" then Prop := Get_Data (Inst, Logger_Data); Set_Return_Value (Data, Active (Logger_Properties (Prop).Handle)); elsif Command = "check" then begin Prop := Get_Data (Inst, Logger_Data); Assert (Logger_Properties (Prop).Handle, Condition => Nth_Arg (Data, 2), Error_Message => Nth_Arg (Data, 3), Message_If_Success => Nth_Arg (Data, 4, "")); exception when System.Assertions.Assert_Failure => Set_Error_Msg (Data, "Assertion error: " & Nth_Arg (Data, 3)); end; elsif Command = "count" then Prop := Get_Data (Inst, Logger_Data); Set_Return_Value (Data, Count (Logger_Properties (Prop).Handle)); end if; end Logger_Handler; ---------------------------- -- Register_Console_Class -- ---------------------------- procedure Register_Console_Class (Repo : access Scripts_Repository_Record'Class; Class : Class_Type) is begin Register_Command (Repo, "write", Params => (Param ("text"), Param ("mode", Optional => True)), Class => Class, Handler => Console_Command_Handler'Access); Register_Command (Repo, "clear", Class => Class, Handler => Console_Command_Handler'Access); Register_Command (Repo, "flush", Class => Class, Handler => Console_Command_Handler'Access); Register_Command (Repo, "isatty", Class => Class, Handler => Console_Command_Handler'Access); Register_Command (Repo, "read", Params => (1 => Param ("size", Optional => True)), Class => Class, Handler => Console_Command_Handler'Access); Register_Command (Repo, "readline", Params => (1 => Param ("size", Optional => True)), Class => Class, Handler => Console_Command_Handler'Access); end Register_Console_Class; --------------------------- -- Register_Logger_Class -- --------------------------- procedure Register_Logger_Class (Repo : access Scripts_Repository_Record'Class; Class : Class_Type) is begin Register_Command (Repo, Constructor_Method, Params => (1 => Param ("name")), Class => Class, Handler => Logger_Handler'Access); Register_Command (Repo, "log", Params => (1 => Param ("message")), Class => Class, Handler => Logger_Handler'Access); Register_Command (Repo, "set_active", Params => (1 => Param ("active")), Class => Class, Handler => Logger_Handler'Access); Register_Property (Repo, "active", Class => Class, Getter => Logger_Handler'Access); Register_Command (Repo, "check", Params => (1 => Param ("condition"), 2 => Param ("error_message"), 3 => Param ("success_message", Optional => True)), Class => Class, Handler => Logger_Handler'Access); Register_Property (Repo, "count", Class => Class, Getter => Logger_Handler'Access); end Register_Logger_Class; end GNATCOLL.Scripts.Impl; gnatcoll-core-21.0.0/src/gnatcoll-scripts-utils.adb0000644000175000017500000002207613661715457022100 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Unchecked_Deallocation; with GNAT.Strings; use GNAT.Strings; package body GNATCOLL.Scripts.Utils is ------------------------------------ -- Argument_List_To_Quoted_String -- ------------------------------------ function Argument_List_To_Quoted_String (Args : GNAT.Strings.String_List; Quote : Character := '"'; Quote_Backslash : Boolean := True) return String is Len : Natural := 1; begin -- Compute the maximum length of the output for J in Args'Range loop -- For each argument we append at most 3 characters, two quotes -- plus an ending space. if Args (J) /= null then Len := Len + Args (J)'Length + 3; for T in Args (J)'Range loop if Args (J)(T) = Quote or else Args (J)(T) = '\' then Len := Len + 1; end if; end loop; end if; end loop; declare Result : String (1 .. Len + 1); Ind : Natural := Result'First; procedure Append (Str : String); -- Append the contents of Str to Result, protecting quote characters ------------ -- Append -- ------------ procedure Append (Str : String) is begin for J in Str'Range loop if Str (J) = Quote or else (Quote_Backslash and then Str (J) = '\') then Result (Ind) := '\'; Result (Ind + 1) := Str (J); Ind := Ind + 2; else Result (Ind) := Str (J); Ind := Ind + 1; end if; end loop; end Append; begin for J in Args'Range loop if Args (J) /= null then if Index (Args (J).all, " ") > 0 then Result (Ind) := Quote; Ind := Ind + 1; Append (Args (J).all); Result (Ind) := Quote; Result (Ind + 1) := ' '; Ind := Ind + 2; else Append (Args (J).all); Result (Ind) := ' '; Ind := Ind + 1; end if; end if; end loop; return Result (1 .. Ind - 1); end; end Argument_List_To_Quoted_String; ------------------------------- -- Argument_To_Quoted_String -- ------------------------------- function Argument_To_Quoted_String (Arg : String; Quote : Character := '"'; Quote_Backslash : Boolean := True) return String is A : aliased String := Arg; L : constant String_List (1 .. 1) := (1 => A'Unchecked_Access); begin return Argument_List_To_Quoted_String (L, Quote, Quote_Backslash); end Argument_To_Quoted_String; ------------------------------------------------ -- Argument_String_To_List_With_Triple_Quotes -- ------------------------------------------------ function Argument_String_To_List_With_Triple_Quotes (Arg_String : String) return String_List_Access is Max_Args : Integer := 128; New_Argv : String_List_Access := new String_List (1 .. Max_Args); New_Argc : Natural := 0; Idx : Integer; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (String_List, String_List_Access); Backslashed : Boolean; Quoted : Boolean; Triple_Quoted : Boolean; Has_Triple : Boolean; Start_Idx : Integer; Start_With_Triple : Boolean; End_With_Triple : Boolean; begin Idx := Arg_String'First; loop exit when Idx > Arg_String'Last; Backslashed := False; Quoted := False; Triple_Quoted := False; Start_Idx := Idx; Start_With_Triple := False; End_With_Triple := False; while Idx <= Arg_String'Last and then (Backslashed or else Quoted or else Triple_Quoted or else Arg_String (Idx) /= ' ') loop End_With_Triple := False; if Backslashed then Backslashed := False; else case Arg_String (Idx) is when '\' => Backslashed := True; when '"' => if Quoted then Quoted := False; else Has_Triple := Idx + 2 <= Arg_String'Last and then Arg_String (Idx) = '"' and then Arg_String (Idx + 1) = '"' and then Arg_String (Idx + 2) = '"'; if Has_Triple then Triple_Quoted := not Triple_Quoted; if Idx = Start_Idx then Start_With_Triple := Triple_Quoted; end if; End_With_Triple := True; Idx := Idx + 2; else Quoted := True; end if; end if; when others => null; end case; end if; Idx := Idx + 1; end loop; New_Argc := New_Argc + 1; -- Resize the table if needed if New_Argc > Max_Args then declare New_New_Argv : String_List (1 .. Max_Args * 2); begin New_New_Argv (1 .. Max_Args) := New_Argv.all; Unchecked_Free (New_Argv); New_Argv := new String_List'(New_New_Argv); end; Max_Args := Max_Args * 2; end if; if Start_With_Triple and End_With_Triple then New_Argv (New_Argc) := new String'(Arg_String (Start_Idx + 3 .. Idx - 4)); else New_Argv (New_Argc) := new String'(Arg_String (Start_Idx .. Idx - 1)); end if; -- Skip extraneous spaces while Idx <= Arg_String'Last and then Arg_String (Idx) = ' ' loop Idx := Idx + 1; end loop; end loop; declare Result : constant String_List := New_Argv (1 .. New_Argc); begin Unchecked_Free (New_Argv); return new String_List'(Result); end; end Argument_String_To_List_With_Triple_Quotes; --------------- -- Unprotect -- --------------- function Unprotect (Str : String) return String is Result : String (Str'Range); Index : Natural := Result'First; N : Natural := Str'First; begin while N <= Str'Last loop if Str (N) = '\' then if N < Str'Last then Result (Index) := Str (N + 1); end if; N := N + 2; else Result (Index) := Str (N); N := N + 1; end if; Index := Index + 1; end loop; if Result'Length > 1 and then Result (Result'First) = '"' and then Result (Index - 1) = '"' then return Result (Result'First + 1 .. Index - 2); else return Result (Result'First .. Index - 1); end if; end Unprotect; end GNATCOLL.Scripts.Utils; gnatcoll-core-21.0.0/src/gnatcoll-os-constants__unix.ads0000644000175000017500000000561313661715457023127 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L . O S . C O N S T A N T S -- -- -- -- Copyright (C) 2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This is the Unix version of GNATCOLL.OS.Constants package package GNATCOLL.OS.Constants is pragma Pure; ----------------------- -- OS identification -- ----------------------- OS : constant OS_Type := Unix; ------------------------------------- -- File system specific constants -- ------------------------------------- Dir_Sep : constant Character := '/'; -- The character that separates qualified filename components Path_Sep : constant Character := ':'; -- The character that separates paths in a path list Exe_Ext : constant String := ""; -- Executable image extension Default_Casing_Policy : constant Filename_Casing_Policy := Sensitive; -- Default casing policy chosen by the OS ------------------------------------------------ -- Dynamic link libraries specific constants -- ------------------------------------------------ DLL_Name : constant String := "shared library"; -- The OS-specific term to refer to a DLL DLL_Search_Path_Var : constant String := "LD_LIBRARY_PATH"; -- Environment variable used to search for DLLs DLL_Ext : constant String := ".so"; -- DLL image extension end GNATCOLL.OS.Constants; gnatcoll-core-21.0.0/src/gnatcoll-remote.ads0000644000175000017500000000767713661715457020601 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package describes the transport layer used by remote filesystems to -- communicate with a remote host. -- Actual implementation is left to the user application, since it requires -- external knowledge of the environment. -- Spawning commands might require various application-specific info, -- such as the protocol to use (ssh, rsh,...) and possibly require the -- running of commands to setup the environment correctly. These are both -- specific to your application, and this package does not try to perform -- this operation as a result. with GNAT.Expect; with GNAT.Strings; with GNATCOLL.VFS_Types; use GNATCOLL.VFS_Types; package GNATCOLL.Remote is -- Server definition type Server_Record is interface; type Server_Access is access all Server_Record'Class; function Nickname (Server : Server_Record) return String is abstract; function Shell_FS (Server : Server_Record) return FS_Type is abstract; procedure Execute_Remotely (Server : access Server_Record; Args : GNAT.Strings.String_List; Status : out Boolean; Execution_Directory : FS_String := "") is abstract; procedure Execute_Remotely (Server : access Server_Record; Args : GNAT.Strings.String_List; Result : out GNAT.Strings.String_Access; Status : out Boolean; Execution_Directory : FS_String := "") is abstract; -- You must override this subprogram to do the actual spawn of a command on -- the specified remote host. -- Execution_Directory is the directory in which the command must be run. -- The command to execute is passed as the first parameter in Args. Args -- must not be freed by these procedure. procedure Spawn_Remotely (Server : access Server_Record; Descriptor : out GNAT.Expect.Process_Descriptor_Access; Args : GNAT.Strings.String_List) is abstract; -- Spawn a process on the remote machine. -- As opposed to Execute_Remotely, this one does not wait until the -- process as terminated. Instead, it allows users to interact with the -- process by sending commands to it and fetching its output. end GNATCOLL.Remote; gnatcoll-core-21.0.0/src/gnatcoll-promises.ads0000644000175000017500000004672513661715457021144 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2017-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- A promise (also known sometimes as a future or deferred) is a -- synchronization mechanism between asynchronous routines. -- -- Such routines could be implemented as tasks, but also be handled -- via the system's asynchronous I/O primitives, or an event loop in a -- GUI program for instance. -- -- A promise is a value that such a routine can return immediately to the -- caller, before it even starts its processing or the actual value is -- available. The caller can then subscribe to the promise, so that when -- the value becomes actually known, it gets notified and a callback is -- executed. -- -- Here is a simple example: -- -- package Str_Promises is new Promises (String); -- -- type Process_Page is new Str_Promises.Callback with null record; -- overriding procedure On_Next -- (Self : in out Process_Page; Page : String) -- is -- begin -- Put_Line ("Page contents is known: " & Page); -- end On_Next; -- -- P : Str_Promises.Promise; -- -- P := Fetch_URL_Asynchronously ("http://..."); -- P.Subscribe (new Process_Page); -- -- Where Fetch_URL_Asynchronously could run in a task, connect to a -- web server and query a document. -- -- But promises are more interesting when they are chained, i.e. the -- action executed when the first promise is resolved will itself -- return a promise. Here is an example: -- -- package Str_Promises is new Promises (String); -- package Int_Promises is new Promises (Integer); -- package Str_To_Int is new Chains (Str_Promises, Int_Promises); -- use Str_To_Int, Int_Promises; -- -- type Count_Elements is new Str_To_Int.Callback with null record; -- overriding procedure On_Next -- (Self : in out Process_Page; -- Page : String; -- Output : in out Int_Promises.Promise); -- -- For instance, count the number of elements in the XML, and -- -- would call Output.Set_Value -- -- type Report_Count is new Int_Promises.Callback with null record; -- overriding procedure On_Next -- (Self : in out Report_Count; -- Count : Integer); -- -- For instance display the number of elements in a GUI -- -- Subscribe -- (Fetch_URL_Asynchronously ("http://...") -- and new Count_Elements -- and new Report_Count); -- -- The code above returns immediately, even though the URL will be fetched -- in the background (which could take a few seconds), then parsed to count -- the number of elements (which could be done in a separate task and take -- a few milliseconds), and finally this count will be displayed in a GUI. -- -- The advantage of this code is that it is composed of small, independent -- building blocks that are executed when data becomes available. The caller -- does not have to take care of the synchronization, since the promises -- handle that. -- -- Behavior -- ======== -- -- A Promise is a concept that has been adopted by multiple programming -- languages, starting with javascript and C++ (promises and futures). -- -- There are some standard behaviors associated with promises, which this -- package tries to conform with: -- -- * A promise can be in one of three states: -- - Pending: the promise has no associated value yet. Some subprogram is -- still running in the background to fetch that value. -- - Resolved: the routine has successfully finished running, and given -- an actual value to the promise (via a call to Set_Value) -- - Failed: the routine failed, and no value will ever be provided to the -- routine. A reason for the failure is provided via a call -- to Set_Error) -- -- * Any number of callbacks can be set on a routine. They will all be -- executed once when the promise is resolved or failed. They are never -- executed again afterwards. -- -- * A promise can be resolved at any time. Whenever it is resolved, all -- callbacks currently set on the promise are executed and then -- disconnected. It is an error to resolve a promise more than once. -- -- * A callback can be added to a promise at any time. If the promise has -- already been resolved, the callback is executed immediately with the -- value set on that promise. -- -- Tasks -- ===== -- -- Promises are task safe. They can be used from multiple threads (and, as -- always, a single call to Set_Value or Set_Error can be done), subscribed -- to from multiple threads,... -- However, the value itself is under your control. Although the promise -- will only execute one callback at a time, to which is passes the value, -- you should ensure that the value is not used from another thread in -- parallel, or provide appropriate locking. -- Using simple types like Integers or Strings should be safe. -- -- Chaining and callbacks -- ====================== -- -- Promises can be chained, so that the callback for the first promise will -- itself return a promise, whose callback might in turn return a promise, -- and so on. -- -- The syntax to chain promises is: -- -- Subscribe (P and new A and new B and new C; -- -- Let's take the following chain: -- -- P and new A -- A is the callback on P -- and new B -- B is the callback on the promise returned by A -- and new C; -- C is the callback on the promise returned by C -- -- The following callbacks might occur (where *.P is the promise output -- by the routine, *.V is the value of that promise, and *.R is the reason -- for the failure of that promise, which defaults to the reason received -- from the previous release unless overridden): -- -- promises calls -- If P is resolved: A.On_Next (P.V) -- If A.P is resolved: B.On_Next (A.V) -- if B.P is resolved: C.On_Next (B.V) -- else B.P failed: C.On_Error (B.R) -- else A.P failed: B.On_Error (A.R), C.On_Error (B.R) -- else P failed: A.On_Error (P.R), B.On_Error (A.R), -- C.On_Error (B.R) -- -- Q: What if I want multiple callbacks on the same promise ? -- A: You need to use intermediate variables, as in: -- Q := P and new A; -- Subscribe (Q and new B); -- Subscribe (Q and new C); -- Where both B and C are callbacks on A's return promise (and not -- chained together). -- -- A more convenient syntax exists, as in the following example: -- -- P and (new A & new B) -- A.P is passed on to the next step -- and (new C & new D) -- C.P is passed on to the next step -- and new E; -- -- If P is resolved: A.On_Next (P.V), B.On_Next (P.V) -- if A.P is resolved: C.On_Next (A.V), D.On_Next (A.V) -- if C.P is resolved: E.On_Next (C.V) -- else C.P failed: E.On_Error (C.R) -- else A.P failed: C.On_Error (A.R), D.On_Error (A.R), -- E.On_Error (C.R) -- else P failed: A.On_Error (P.R), B.On_Error (P.R), -- C.On_Error (A.R), D.On_Error (A.R), -- E.On_Error (C.R) -- -- Note that there is no guaranteed order in which the callbacks are -- executed, so for instance it is possible that C.On_Next and -- E.On_Next are called before B.On_Next. -- -- Q: What if I want different success and failure callbacks ? -- A: A callback is an object with both a On_Next and a On_Error primitive -- operations. So you could set two different callbacks on the same -- promise (as we did above in the first question) with GNATCOLL.Atomic; with GNATCOLL.Refcount; package GNATCOLL.Promises is use type GNATCOLL.Atomic.Atomic_Counter; subtype Promise_State is GNATCOLL.Atomic.Atomic_Counter; Pending : constant Promise_State := 0; Resolved : constant Promise_State := 1; Failed : constant Promise_State := 2; Resolving : constant Promise_State := 3; Failing : constant Promise_State := 4; Subscribing : constant Promise_State := 5; subtype Actual_Promise_State is Promise_State range Pending .. Subscribing; -- The various states that a promise can have. -- We use atomic operations when possible to manipulate it, to make -- promises task safe. type Promise_Chain is tagged private; procedure Subscribe (Self : Promise_Chain) with Inline => True; -- A dummy type used when chaining promises with the "and" -- operator. See below for an example of code. -- -- Do not mark this procedure as "is null", since otherwise GNAT -- does not even call the last "and" in the chain. -------------- -- IFreeable -- -------------- type IFreeable is interface; type Freeable_Access is access all IFreeable'Class; -- a general interface for objects that have an explicit Free -- primitive operation. procedure Free (Self : in out IFreeable) is null; -- Free internal data of Self procedure Free (Self : in out Freeable_Access); -- Free self, via its primitive operation, and then free the pointer ---------- -- Impl -- ---------- -- This package is for implementation details package Impl is type IPromise_Data is interface; procedure Free (Self : in out IPromise_Data) is null; procedure Dispatch_Free (Self : in out IPromise_Data'Class); type IAbstract_Promise is interface; package Promise_Pointers is new GNATCOLL.Refcount.Shared_Pointers (Element_Type => IPromise_Data'Class, Release => Dispatch_Free, Atomic_Counters => True); -- thread-safe type Root_Promise is new Promise_Pointers.Ref and IAbstract_Promise with null record; type IPromise_Callback is interface and IFreeable; type Promise_Callback_Access is access all IPromise_Callback'Class; procedure On_Error (Self : in out IPromise_Callback; Reason : String) is null; -- Called when a promise has failed and will never be resolved. end Impl; -------------- -- Promises -- -------------- generic type T (<>) is private; package Promises is type Promise is new Impl.IAbstract_Promise with private; -- A promise is a smart pointer: it is a wrapper around shared -- data that is freed when no more reference to the promise -- exists. subtype Result_Type is T; --------------- -- Callbacks -- --------------- type Callback is interface and Impl.IPromise_Callback; type Callback_Access is access all Callback'Class; procedure On_Next (Self : in out Callback; R : Result_Type) is null; -- Executed when a promise is resolved. It provides the real value -- associated with the promise. type Callback_List (<>) is private; -- Multiple callbacks, all subscribed to the same promise (or -- will be subscribed to the same promise). -------------- -- Promises -- -------------- function Create return Promise with Post => Create'Result.Is_Created and Create'Result.Get_State = Pending; -- Create a new promise, with no associated value. procedure Set_Value (Self : in out Promise; R : T) with Pre => Self.Is_Created and Self.Get_State /= Resolved and Self.Get_State /= Failed, Post => Self.Get_State = Resolved; -- Give a result to the promise. -- The callbacks' On_Next methods are executed. -- This can only be called once on a promise. procedure Set_Error (Self : in out Promise; Reason : String) with Pre => Self.Is_Created and Self.Get_State /= Resolved and Self.Get_State /= Failed, Post => Self.Get_State = Failed; -- Mark the promise has failed. It will never be resolved. -- The callbacks' On_Error method are executed. procedure Subscribe (Self : Promise; Cb : not null access Callback'Class) with Pre => Self.Is_Created; function "and" (Self : Promise; Cb : not null access Callback'Class) return Promise_Chain with Pre => Self.Is_Created; function "and" (Self : Promise; Cb : Callback_List) return Promise_Chain with Pre => Self.Is_Created; -- Will call Cb when Self is resolved or failed (or immediately if Self -- has already been resolved or failed). -- Any number of callbacks can be set on each promise. -- If you want to chain promises (i.e. your callback itself returns -- a promise), take a look at the Chains package below. -- -- Cb must be allocated specifically for this call, and will be -- freed as needed. You must not reuse the same pointer for multiple -- calls to Subscribe. -- ??? This is unsafe -- -- Self is modified, but does not need to be "in out" since a promise -- is a pointer. This means that Subscribe can be directly called on -- the result of a function call, for instance. function "&" (Cb : not null access Callback'Class; Cb2 : not null access Callback'Class) return Callback_List; function "&" (List : Callback_List; Cb2 : not null access Callback'Class) return Callback_List; -- Create a list of callbacks that will all be subscribed to the same -- promise. function Is_Created (Self : Promise'Class) return Boolean with Inline; -- Whether the promise has been created function Get_State (Self : Promise'Class) return Actual_Promise_State with Inline; -- Used for pre and post conditions private type Promise is new Impl.Root_Promise with null record; type Callback_List is array (Natural range <>) of not null access Callback'Class; function Is_Created (Self : Promise'Class) return Boolean is (not Self.Is_Null); end Promises; ------------ -- Chains -- ------------ generic with package Input_Promises is new Promises (<>); with package Output_Promises is new Promises (<>); package Chains is type Callback is abstract new Input_Promises.Callback with private; procedure On_Next (Self : in out Callback; Input : Input_Promises.Result_Type; Output : in out Output_Promises.Promise) is abstract with Post'Class => Output.Get_State = Resolved or Output.Get_State = Failed; -- This is the procedure that needs overriding, not the one inherited -- from Input_Promises. When chaining, a callback returns another -- promise, to which the user can attach further callbacks, and so on. -- -- Failures in a promise are by default propagated to the output -- promise, unless you override the Failed primitive operation of -- Self. type Callback_List (<>) is private; function Is_Registered (Self : not null access Callback'Class) return Boolean with Inline; function Is_Registered (Self : Callback_List) return Boolean with Inline; -- Whether the callback has already been set on a promise. It is -- invalid to use the same callback on multiple promises (or even -- multiple times on the same promise). function "and" (Input : Input_Promises.Promise; Cb : not null access Callback'Class) return Output_Promises.Promise with Pre => not Is_Registered (Cb) and Input.Is_Created, Post => Is_Registered (Cb) and "and"'Result.Is_Created; -- Chains two properties. -- When Input is resolved, Cb is executed and will in turn resolve -- the output promise -- These functions return immediately a promise that will be resolved -- later. function "&" (Cb : not null access Callback'Class; Cb2 : not null access Input_Promises.Callback'Class) return Callback_List with Pre => not Is_Registered (Cb); -- ??? Results in GNAT bug box -- Post => "and"'Result = Cb -- and not Is_Registered ("and"'Result); function "&" (List : Callback_List; Cb2 : not null access Input_Promises.Callback'Class) return Callback_List; -- Used to set multiple callbacks on the same promise, as in: -- P & (new A and new B) & new C -- Only Cb is expected to output a promise, which will be -- forwarded to the next step (C in this example). Cb2 only -- gets notified via its On_Next and On_Error primitives. function "and" (Input : Input_Promises.Promise; Cb : Callback_List) return Output_Promises.Promise with Pre => not Is_Registered (Cb) and Input.Is_Created, Post => Is_Registered (Cb) and "and"'Result.Is_Created; -- Chaining multiple callbacks on the same promise private type Callback is abstract new Input_Promises.Callback with record Promise : aliased Output_Promises.Promise; end record; overriding procedure On_Next (Self : in out Callback; P : Input_Promises.Result_Type); overriding procedure On_Error (Self : in out Callback; Reason : String); type Callback_Array is array (Natural range <>) of not null access Input_Promises.Callback'Class; type Callback_List (N : Natural) is record Cb : not null access Callback'Class; Cb2 : Callback_Array (1 .. N); end record; end Chains; private type Promise_Chain is tagged null record; end GNATCOLL.Promises; gnatcoll-core-21.0.0/src/gnatcoll-asserts.adb0000644000175000017500000002111713661715457020732 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2005-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Assertions; package body GNATCOLL.Asserts is ------------------------- -- On_Assertion_Failed -- ------------------------- overriding procedure On_Assertion_Failed (Self : Exception_Reporter; Msg : String; Details : String; Location : String; Entity : String) is pragma Unreferenced (Self); begin raise Ada.Assertions.Assertion_Error with Msg & " " & Details & " at " & Location & " in " & Entity; end On_Assertion_Failed; ------------- -- Asserts -- ------------- package body Asserts is ------------------- -- Assert_Failed -- ------------------- procedure Assert_Failed (Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin Report.On_Assertion_Failed (Msg => Msg, Details => "", Location => Location, Entity => Entity); end Assert_Failed; ------------ -- Equals -- ------------ package body Equals is ------------------ -- Assert_Equal -- ------------------ procedure Assert_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Enabled and then not "=" (Left, Right) then Report.On_Assertion_Failed (Details => Image (Left) & " = " & Image (Right), Msg => Msg, Location => Location, Entity => Entity); end if; end Assert_Equal; ---------------------- -- Assert_Not_Equal -- ---------------------- procedure Assert_Not_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Enabled and then "=" (Left, Right) then Report.On_Assertion_Failed (Details => Image (Left) & " /= " & Image (Right), Msg => Msg, Location => Location, Entity => Entity); end if; end Assert_Not_Equal; end Equals; ------------- -- Compare -- ------------- package body Compare is ------------------ -- Assert_Equal -- ------------------ procedure Assert_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Enabled and then not "=" (Left, Right) then Report.On_Assertion_Failed (Details => Image (Left) & " = " & Image (Right), Msg => Msg, Location => Location, Entity => Entity); end if; end Assert_Equal; ---------------------- -- Assert_Not_Equal -- ---------------------- procedure Assert_Not_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Enabled and then "=" (Left, Right) then Report.On_Assertion_Failed (Details => Image (Left) & " /= " & Image (Right), Msg => Msg, Location => Location, Entity => Entity); end if; end Assert_Not_Equal; ----------------- -- Assert_Less -- ----------------- procedure Assert_Less (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Enabled and then not "<" (Left, Right) then Report.On_Assertion_Failed (Details => Image (Left) & " < " & Image (Right), Msg => Msg, Location => Location, Entity => Entity); end if; end Assert_Less; -------------------------- -- Assert_Less_Or_Equal -- -------------------------- procedure Assert_Less_Or_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Enabled and then not ("<" (Left, Right) or else "=" (Left, Right)) then Report.On_Assertion_Failed (Details => Image (Left) & " <= " & Image (Right), Msg => Msg, Location => Location, Entity => Entity); end if; end Assert_Less_Or_Equal; ----------------------------- -- Assert_Greater_Or_Equal -- ----------------------------- procedure Assert_Greater_Or_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Enabled and then "<" (Left, Right) then Report.On_Assertion_Failed (Details => Image (Left) & " >= " & Image (Right), Msg => Msg, Location => Location, Entity => Entity); end if; end Assert_Greater_Or_Equal; -------------------- -- Assert_Greater -- -------------------- procedure Assert_Greater (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Enabled and then ("<" (Left, Right) or else "=" (Left, Right)) then Report.On_Assertion_Failed (Details => Image (Left) & " > " & Image (Right), Msg => Msg, Location => Location, Entity => Entity); end if; end Assert_Greater; end Compare; end Asserts; end GNATCOLL.Asserts; gnatcoll-core-21.0.0/src/gnatcoll-scripts-projects.ads0000644000175000017500000000611013661715457022601 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Projects; package GNATCOLL.Scripts.Projects is type Project_Tree_Retriever is abstract tagged null record; -- This type provides access to project tree for scripting API not overriding function Get_Project_Tree (Self : Project_Tree_Retriever) return GNATCOLL.Projects.Project_Tree_Access is abstract; -- Get current project tree function Project_Tree return GNATCOLL.Projects.Project_Tree_Access; -- Get project tree from assigned project tree retriever pointer procedure Register_Commands (Repo : not null access Scripts_Repository_Record'Class; Value : not null access Project_Tree_Retriever'Class); -- Add script commands for Project class and an object to access project -- tree from scripting API. -- Next subprogram could be useful to define new commands function Get_Project_Class (Repo : access Scripts_Repository_Record'Class) return Class_Type; -- Return the class to use for projects. This encapsulates a Project_Type function Get_Data (Data : Callback_Data'Class; N : Positive) return GNATCOLL.Projects.Project_Type; -- Retrieve some project information in Instance function Create_Project (Script : access Scripting_Language_Record'Class; Project : GNATCOLL.Projects.Project_Type) return Class_Instance; -- Return a new project end GNATCOLL.Scripts.Projects; gnatcoll-core-21.0.0/src/gnatcoll-scripts-utils.ads0000644000175000017500000000710713661715457022117 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNAT.Strings; package GNATCOLL.Scripts.Utils is use type GNAT.Strings.String_List_Access; function Argument_List_To_Quoted_String (Args : GNAT.Strings.String_List; Quote : Character := '"'; Quote_Backslash : Boolean := True) return String; -- Return the arguments as a full string. -- Arguments that contain spaces but do not already contain quotes -- will be put into quotes. -- Backslashes are duplicated if Quote_Baskslash is True. -- The result of this subprogram on the string A simple\ "string" -- is: Quote_Backslash => "A simple\\ \"string\"" -- not Quote_Backslash => "A simple\ \"string\"" function Argument_To_Quoted_String (Arg : String; Quote : Character := '"'; Quote_Backslash : Boolean := True) return String; -- As above but for a single argument function Argument_String_To_List_With_Triple_Quotes (Arg_String : String) return GNAT.Strings.String_List_Access with Post => Argument_String_To_List_With_Triple_Quotes'Result /= null; -- This is similar to GNAT.OS_Lib.Argument_String_To_List, except that -- if part of the string is surrounded by triple quotes, any special -- character is ignored till the closing triple quotes. This is the same -- behavior as in Python, and is needed for easier quoting of string. -- -- Here is the output in some cases: -- "foo" -> "foo" (quotes preserved) -- """foo""" -> foo (quotes removed when at beginning and end) -- ("""foo""") -> ("""foo""") (quotes preserved in middle) -- foo\"foo -> foo\"foo (backslash not removed from output) function Unprotect (Str : String) return String; -- Remove the \ protections in Str -- ??? This seems to also remove the quotes around an argument end GNATCOLL.Scripts.Utils; gnatcoll-core-21.0.0/src/gnatcoll-memory.adb0000644000175000017500000002501613661715457020560 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2005-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Exceptions; with System.Storage_Elements; use System.Storage_Elements; pragma Warnings (Off); with System.CRTL; -- Force a user's s-memory.adb to be part of the link, -- otherwise s-memory.o will simply be ignored by gprbuild with System.Memory; pragma Warnings (On); package body GNATCOLL.Memory is Memory_Pool : GNAT.Debug_Pools.Debug_Pool; use Ada.Exceptions; Memory_Monitor : Boolean := False; Memory_Check : Boolean := False; Debug_Pool_Initialization_Needed : Boolean := True; procedure Initialize_System_Memory_Debug_Pool (Has_Unhandled_Memory : Boolean := False); -- If not already done, let Debug_Pools know that System.Memory will use -- Debug_Pools, and have to handle memory allocated by System.CRTL package. ----------------------------------------- -- Initialize_System_Memory_Debug_Pool -- ----------------------------------------- procedure Initialize_System_Memory_Debug_Pool (Has_Unhandled_Memory : Boolean := False) is begin if Debug_Pool_Initialization_Needed then Debug_Pool_Initialization_Needed := False; GNAT.Debug_Pools.System_Memory_Debug_Pool (Has_Unhandled_Memory); end if; end Initialize_System_Memory_Debug_Pool; ----------- -- Alloc -- ----------- function Alloc (Size : size_t) return System.Address is Result : System.Address; Actual_Size : size_t := Size; begin if Size = size_t'Last then Raise_Exception (Storage_Error'Identity, "object too large"); end if; -- Change size from zero to non-zero. We still want a proper pointer -- for the zero case because pointers to zero length objects have to -- be distinct, but we can't just go ahead and allocate zero bytes, -- since some malloc's return zero for a zero argument. if Size = 0 then Actual_Size := 1; end if; if Memory_Monitor then Initialize_System_Memory_Debug_Pool; Memory_Pool.Allocate (Storage_Address => Result, Size_In_Storage_Elements => Storage_Count (Actual_Size), Alignment => Standard'Maximum_Alignment); else Result := System.CRTL.malloc (System.CRTL.size_t (Actual_Size)); end if; if Result = System.Null_Address then Raise_Exception (Storage_Error'Identity, "heap exhausted"); end if; return Result; end Alloc; ---------- -- Free -- ---------- procedure Free (Ptr : System.Address) is begin if Ptr /= System.Null_Address and not Memory_Check then if Memory_Monitor then Initialize_System_Memory_Debug_Pool; Memory_Pool.Deallocate (Storage_Address => Ptr, Size_In_Storage_Elements => Storage_Count'Last, Alignment => Standard'Maximum_Alignment); else System.CRTL.free (Ptr); end if; end if; end Free; ------------- -- Realloc -- ------------- function Realloc (Ptr : System.Address; Size : size_t) return System.Address is Result : System.Address; Actual_Size : size_t := Size; begin if Size = size_t'Last then Raise_Exception (Storage_Error'Identity, "object too large"); end if; if not Memory_Monitor then Result := System.CRTL.realloc (Ptr, System.CRTL.size_t (Actual_Size)); else declare Size_Was : Storage_Count; Valid : Boolean; procedure Memmove (Dest : System.Address; Src : System.Address; N : size_t); pragma Import (C, Memmove, "memmove"); begin Initialize_System_Memory_Debug_Pool; Get_Size (Storage_Address => Ptr, Size_In_Storage_Elements => Size_Was, Valid => Valid); if not Valid then declare Reallocated : System.Address; begin Reallocated := System.CRTL.realloc (Ptr, System.CRTL.size_t (Actual_Size)); Memory_Pool.Allocate (Storage_Address => Result, Size_In_Storage_Elements => Storage_Count (Actual_Size), Alignment => Standard'Maximum_Alignment); Memmove (Dest => Result, Src => Reallocated, N => Actual_Size); System.CRTL.free (Reallocated); end; else Memory_Pool.Allocate (Storage_Address => Result, Size_In_Storage_Elements => Storage_Count (Size), Alignment => Standard'Maximum_Alignment); if size_t (Size_Was) < Actual_Size then Actual_Size := size_t (Size_Was); end if; Memmove (Dest => Result, Src => Ptr, N => Actual_Size); Free (Ptr); end if; end; end if; if Result = System.Null_Address then Raise_Exception (Storage_Error'Identity, "heap exhausted"); end if; return Result; end Realloc; ----------------------- -- Redirectable_Dump -- ----------------------- procedure Redirectable_Dump (Size : Positive; Report : Report_Type := All_Reports) is procedure Redirected_Dump is new GNAT.Debug_Pools.Dump (Put_Line => Put_Line, Put => Put); begin Redirected_Dump (Memory_Pool, Size => Size, Report => GNAT.Debug_Pools.Report_Type (Report)); end Redirectable_Dump; procedure Dump (Size : Positive; Report : Report_Type := All_Reports) is begin Memory_Pool.Dump_Stdout (Size => Size, Report => GNAT.Debug_Pools.Report_Type (Report)); end Dump; ------------------------- -- Get_Ada_Allocations -- ------------------------- function Get_Ada_Allocations return Watermark_Info is begin return (High => GNATCOLL.Memory.Byte_Count (Memory_Pool.High_Water_Mark), Current => GNATCOLL.Memory.Byte_Count (Memory_Pool.Current_Water_Mark) ); end Get_Ada_Allocations; --------------------- -- Get_Allocations -- --------------------- function Get_Allocations return Watermark_Info is function Get_Peak_RSS return size_t; pragma Import (C, Get_Peak_RSS, "gnatcoll_getPeakRSS"); function Get_Current_RSS return size_t; pragma Import (C, Get_Current_RSS, "gnatcoll_getCurrentRSS"); begin return (High => Byte_Count (Get_Peak_RSS), Current => Byte_Count (Get_Current_RSS)); end Get_Allocations; ----------- -- Reset -- ----------- procedure Reset is begin GNAT.Debug_Pools.Reset; end Reset; --------------- -- Configure -- --------------- procedure Configure (Activate_Monitor : Boolean := False; Disable_Free : Boolean := False; Stack_Trace_Depth : Natural := 30; Maximum_Logically_Freed_Memory : Long_Long_Integer := 50_000_000; Minimum_To_Free : Long_Long_Integer := 0; Reset_Content_On_Free : Boolean := True; Raise_Exceptions : Boolean := False; Advanced_Scanning : Boolean := False; Errors_To_Stdout : Boolean := True; Low_Level_Traces : Boolean := False) is begin Memory_Check := Disable_Free; if Activate_Monitor and not Memory_Monitor then Initialize_System_Memory_Debug_Pool (Has_Unhandled_Memory => True); Memory_Monitor := True; end if; if Memory_Monitor then Memory_Pool.Configure (Stack_Trace_Depth => Stack_Trace_Depth, Maximum_Logically_Freed_Memory => SSC (Maximum_Logically_Freed_Memory), Minimum_To_Free => SSC (Minimum_To_Free + 1), Reset_Content_On_Free => Reset_Content_On_Free, Raise_Exceptions => Raise_Exceptions, Advanced_Scanning => Advanced_Scanning, Errors_To_Stdout => Errors_To_Stdout, Low_Level_Traces => Low_Level_Traces); end if; end Configure; -------------------- -- Mark_Traceback -- -------------------- procedure Mark_Traceback is Size_Was : Byte_Count; pragma Unreferenced (Size_Was); begin null; end Mark_Traceback; end GNATCOLL.Memory; gnatcoll-core-21.0.0/src/gnatcoll-io.adb0000644000175000017500000000522713661715457017661 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Unchecked_Deallocation; package body GNATCOLL.IO is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (File_Record'Class, File_Access); --------- -- Ref -- --------- procedure Ref (File : File_Access) is begin File.Ref_Count := File.Ref_Count + 1; end Ref; ----------- -- Unref -- ----------- procedure Unref (File : in out File_Access) is begin if File.Ref_Count > 0 then File.Ref_Count := File.Ref_Count - 1; if File.Ref_Count = 0 then Destroy (File.all); Unchecked_Free (File); end if; end if; end Unref; ------------- -- Destroy -- ------------- procedure Destroy (File : in out File_Record) is begin Free (File.Full); if File.Normalized_And_Resolved /= File.Normalized then Free (File.Normalized_And_Resolved); end if; Free (File.Normalized); end Destroy; end GNATCOLL.IO; gnatcoll-core-21.0.0/src/gnatcoll-mmap-system__win32.adb0000644000175000017500000002451013661715457022703 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2007-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.IO_Exceptions; with System; use System; with GNAT.Strings; use GNAT.Strings; with GNATCOLL.IO.Native; use GNATCOLL.IO.Native; package body GNATCOLL.Mmap.System is use Win; function Align (Addr : File_Size) return File_Size; -- Align some offset/length to the lowest page boundary function Open_Common (Filename : String; Use_Mmap_If_Available : Boolean; Write : Boolean) return System_File; ----------------- -- Open_Common -- ----------------- function Open_Common (Filename : String; Use_Mmap_If_Available : Boolean; Write : Boolean) return System_File is dwDesiredAccess, dwShareMode : DWORD; PageFlags : DWORD; W_Filename : constant Wide_String := Codec.From_UTF8 (Filename) & Wide_Character'Val (0); File_Handle, Mapping_Handle : HANDLE; Size : aliased LARGE_INTEGER; Status : BOOL; begin if Write then dwDesiredAccess := GENERIC_READ + GENERIC_WRITE; dwShareMode := 0; PageFlags := Win.PAGE_READWRITE; else dwDesiredAccess := GENERIC_READ; dwShareMode := Win.FILE_SHARE_READ; PageFlags := Win.PAGE_READONLY; end if; -- Actually open the file File_Handle := CreateFile (W_Filename'Address, dwDesiredAccess, dwShareMode, null, OPEN_EXISTING, Win.FILE_ATTRIBUTE_NORMAL, 0); if File_Handle = Win.INVALID_HANDLE_VALUE then raise Ada.IO_Exceptions.Name_Error with "Cannot open " & Filename; end if; -- Compute its size Status := Win.GetFileSizeEx (File_Handle, Size'Access); if Status = Win.FALSE then -- Raise an error either if we can't compute the size. raise Ada.IO_Exceptions.Use_Error with "cannot compute file size"; end if; if File_Size'Size <= 32 and then Size > 2 ** 32 then -- Likewise if user tries to map a file for which size is superior -- than 4GB on 32bits systems. In theory this is supported but this -- would require change in gnatcoll.mmap API. raise Ada.IO_Exceptions.Use_Error with "cannot open file >4GB"; end if; -- Then create a mapping object, if needed. On Win32, file memory -- mapping is always available. if Use_Mmap_If_Available then Mapping_Handle := Win.CreateFileMapping (File_Handle, null, PageFlags, DWORD (Size / 2 ** 32), DWORD (Size mod 2 ** 32), Standard.System.Null_Address); else Mapping_Handle := Win.INVALID_HANDLE_VALUE; end if; -- Note that conversioin to File_Size is safe at this stage because of -- previous checks. return (Handle => File_Handle, Mapped => Use_Mmap_If_Available, Mapping_Handle => Mapping_Handle, Write => Write, Length => File_Size (Size)); end Open_Common; --------------- -- Open_Read -- --------------- function Open_Read (Filename : String; Use_Mmap_If_Available : Boolean := True) return System_File is begin return Open_Common (Filename, Use_Mmap_If_Available, False); end Open_Read; ---------------- -- Open_Write -- ---------------- function Open_Write (Filename : String; Use_Mmap_If_Available : Boolean := True) return System_File is begin return Open_Common (Filename, Use_Mmap_If_Available, True); end Open_Write; ----------- -- Close -- ----------- procedure Close (File : in out System_File) is Ignored : BOOL; pragma Unreferenced (Ignored); begin Ignored := CloseHandle (File.Mapping_Handle); Ignored := CloseHandle (File.Handle); File.Handle := Win.INVALID_HANDLE_VALUE; File.Mapping_Handle := Win.INVALID_HANDLE_VALUE; end Close; -------------------- -- Read_From_Disk -- -------------------- function Read_From_Disk (File : System_File; Offset, Length : File_Size) return GNAT.Strings.String_Access is Buffer : String_Access := new String (1 .. Integer (Length)); Pos : DWORD; NbRead : aliased DWORD; pragma Unreferenced (Pos); begin Pos := Win.SetFilePointer (File.Handle, Win.LONG (Offset), null, Win.FILE_BEGIN); if Win.ReadFile (File.Handle, Buffer.all'Address, DWORD (Length), NbRead'Unchecked_Access, null) = Win.FALSE then GNAT.Strings.Free (Buffer); raise Ada.IO_Exceptions.Device_Error; end if; return Buffer; end Read_From_Disk; ------------------- -- Write_To_Disk -- ------------------- procedure Write_To_Disk (File : System_File; Offset, Length : File_Size; Buffer : GNAT.Strings.String_Access) is Pos : DWORD; NbWritten : aliased DWORD; pragma Unreferenced (Pos); begin pragma Assert (File.Write); Pos := Win.SetFilePointer (File.Handle, Win.LONG (Offset), null, Win.FILE_BEGIN); if Win.WriteFile (File.Handle, Buffer.all'Address, DWORD (Length), NbWritten'Unchecked_Access, null) = Win.FALSE then raise Ada.IO_Exceptions.Device_Error; end if; end Write_To_Disk; -------------------- -- Create_Mapping -- -------------------- procedure Create_Mapping (File : System_File; Offset, Length : in out File_Size; Mutable : Boolean; Mapping : out System_Mapping; Advice : Use_Advice := Use_Normal) is pragma Unreferenced (Advice); Flags : DWORD; Offset64 : LARGE_INTEGER; begin if File.Write then Flags := Win.FILE_MAP_WRITE; elsif Mutable then Flags := Win.FILE_MAP_COPY; else Flags := Win.FILE_MAP_READ; end if; -- Check that limits are valid regarding overflow and/or file size. if Offset > File_Size'Last - Length or else Offset + Length > File.Length then raise Ada.IO_Exceptions.Use_Error with "Invalid mapping limits"; end if; -- Adjust offset and mapping length to account for the required -- alignment of offset on page boundary. declare Queried_Offset : constant File_Size := Offset; begin Offset := Align (Offset); -- First extend the length to compensate the offset shift, then align -- it on the upper page boundary, so that the whole queried area is -- covered. -- By construction Align return an integer >= 0 lower than the -- original one. As consequence the following 2 statements cannot -- overflow. Addition of Get_Page_Size to length is done afterwards -- to avoid possible overflow. Length := Length + Queried_Offset - Offset; Length := Align (Length - 1); -- But do not exceed the length of the file -- By construction File.Length - Offset - Length is >=0 (no overflow) if Get_Page_Size > File.Length - Offset - Length then Length := File.Length - Offset; else Length := Length + Get_Page_Size; end if; end; -- Force conversion of Offset to 64bits format in order to able to -- split it in two DWORD on both 32bits and 64bits systems. Offset64 := LARGE_INTEGER (Offset); Mapping := (Address => Win.MapViewOfFile (File.Mapping_Handle, Flags, DWORD (Offset64 / 2 ** 32), DWORD (Offset64 mod 2 ** 32), SIZE_T (Length)), Length => Length); end Create_Mapping; --------------------- -- Dispose_Mapping -- --------------------- procedure Dispose_Mapping (Mapping : in out System_Mapping) is Ignored : BOOL; pragma Unreferenced (Ignored); begin Ignored := Win.UnmapViewOfFile (Mapping.Address); Mapping := Invalid_System_Mapping; end Dispose_Mapping; ------------------- -- Get_Page_Size -- ------------------- function Get_Page_Size return File_Size is SystemInfo : aliased SYSTEM_INFO; begin GetSystemInfo (SystemInfo'Unchecked_Access); return File_Size (SystemInfo.dwAllocationGranularity); end Get_Page_Size; ----------- -- Align -- ----------- function Align (Addr : File_Size) return File_Size is begin return Addr - Addr mod Get_Page_Size; end Align; end GNATCOLL.Mmap.System; gnatcoll-core-21.0.0/src/gnatcoll-path.adb0000644000175000017500000004521313661715457020205 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Characters.Handling; use Ada.Characters.Handling; with GNAT.OS_Lib; use GNAT.OS_Lib; package body GNATCOLL.Path is function Dir_Separator (FS : FS_Type) return Character; -- return '/' or '\' depending of FS ------------------- -- Dir_Separator -- ------------------- function Dir_Separator (FS : FS_Type) return Character is begin case FS is when FS_Unix | FS_Unix_Case_Insensitive => return '/'; when FS_Windows => return '\'; when FS_Unknown => raise Invalid_Filesystem; end case; end Dir_Separator; function Internal_Local_FS return FS_Type; -- We cache this value as it is very often referenced... and is -- a constant ! ----------------------- -- Internal_Local_FS -- ----------------------- function Internal_Local_FS return FS_Type is function Get_File_Names_Case_Sensitive return Integer; pragma Import (C, Get_File_Names_Case_Sensitive, "__gnat_get_file_names_case_sensitive"); begin if GNAT.OS_Lib.Directory_Separator = '\' then return FS_Windows; else if Get_File_Names_Case_Sensitive = 0 then return FS_Unix_Case_Insensitive; else return FS_Unix; end if; end if; end Internal_Local_FS; Loc_FS : constant FS_Type := Internal_Local_FS; -------------- -- Local_FS -- -------------- function Local_FS return FS_Type is begin return Loc_FS; end Local_FS; ----------------------- -- Is_Case_Sensitive -- ----------------------- function Is_Case_Sensitive (FS : FS_Type) return Boolean is begin case FS is when FS_Unix => return True; when FS_Windows | FS_Unix_Case_Insensitive => return False; when FS_Unknown => raise Invalid_Filesystem; end case; end Is_Case_Sensitive; ----------------- -- Has_Devices -- ----------------- function Has_Devices (FS : FS_Type) return Boolean is begin case FS is when FS_Unix | FS_Unix_Case_Insensitive => return False; when FS_Windows => return True; when FS_Unknown => raise Invalid_Filesystem; end case; end Has_Devices; --------------------------- -- Multi_Unit_Index_Char -- --------------------------- function Multi_Unit_Index_Char (FS : FS_Type) return Character is pragma Unreferenced (FS); begin return '~'; end Multi_Unit_Index_Char; ------------------- -- Exe_Extension -- ------------------- function Exe_Extension (FS : FS_Type) return FS_String is begin case FS is when FS_Unix | FS_Unix_Case_Insensitive => return ""; when FS_Windows => return ".exe"; when FS_Unknown => raise Invalid_Filesystem; end case; end Exe_Extension; -------------- -- Get_Root -- -------------- function Get_Root (FS : FS_Type; Path : FS_String) return FS_String is Found_One : Boolean; begin case FS is when FS_Unix | FS_Unix_Case_Insensitive => return "/"; when FS_Windows => if Path'Length >= 2 and then Path (Path'First + 1) = ':' then return Path (Path'First) & ":\"; elsif Path'Length > 3 and then Path (Path'First .. Path'First + 1) = "\\" then -- We need to return "\\machine\svc\" in this case Found_One := False; -- Used to determine that we found at least one -- '\' after the initial "\\" for J in Path'First + 2 .. Path'Last loop if Path (J) = '\' then if not Found_One then Found_One := True; else return Path (Path'First .. J); end if; end if; end loop; if Found_One then -- Case where we had "\\machine\svc" to analyse. The root -- is then "\\machine\src\" return Path & '\'; else -- Incomplete ... return the default return "\"; end if; else return "\"; end if; when FS_Unknown => raise Invalid_Filesystem; end case; end Get_Root; ---------------------- -- Is_Absolute_Path -- ---------------------- function Is_Absolute_Path (FS : FS_Type; Path : FS_String) return Boolean is begin if Path'Length = 0 then return False; end if; case FS is when FS_Unix | FS_Unix_Case_Insensitive => return Path (Path'First) = '/'; when FS_Windows => if Path'Length >= 3 and then Path (Path'First + 1 .. Path'First + 2) = ":\" then return True; elsif Path'Length > 1 and then (Path (Path'First) = '\' or else Path (Path'First) = '/') then return True; end if; when FS_Unknown => raise Invalid_Filesystem; end case; return False; end Is_Absolute_Path; ---------- -- Path -- ---------- function Path (FS : FS_Type; Device : FS_String; Dir : FS_String; File : FS_String) return FS_String is Has_Dirsep : constant Boolean := Dir'Length >= 1 and then Dir (Dir'Last) = Dir_Separator (FS); begin if FS = FS_Unknown then raise Invalid_Filesystem; end if; if FS in FS_Unix .. FS_Unix_Case_Insensitive or else Device = "" then if Has_Dirsep then return Dir & From_Unix (FS, File); else return Dir & Dir_Separator (FS) & From_Unix (FS, File); end if; else if Has_Dirsep then return Device & ":" & Dir & From_Unix (FS, File); else return Device & ":" & Dir & Dir_Separator (FS) & From_Unix (FS, File); end if; end if; end Path; ----------- -- Equal -- ----------- function Equal (FS : FS_Type; Path1, Path2 : FS_String) return Boolean is function Internal (S1, S2 : FS_String) return Boolean; -- Compare, taking care of trailing dir separators -------------- -- Internal -- -------------- function Internal (S1, S2 : FS_String) return Boolean is begin return S1 = S2 or else (S2'Length > 0 and then S2 (S2'Last) = Dir_Separator (FS) and then S1 = S2 (S2'First .. S2'Last - 1)) or else (S1'Length > 0 and then S1 (S1'Last) = Dir_Separator (FS) and then S2 = S1 (S1'First .. S1'Last - 1)); end Internal; begin if Is_Case_Sensitive (FS) then return Internal (Path1, Path2); else return Internal (FS_String (To_Lower (String (Path1))), FS_String (To_Lower (String (Path2)))); end if; end Equal; ------------- -- To_Unix -- ------------- function To_Unix (FS : FS_Type; Path : FS_String; Cygwin_Path : Boolean := False) return FS_String is begin case FS is when FS_Unix | FS_Unix_Case_Insensitive => return Path; when FS_Windows => declare Ret : FS_String := Path; begin for J in Ret'Range loop if Ret (J) = '\' then Ret (J) := '/'; end if; end loop; if Cygwin_Path and then Ret'Length > 2 and then Ret (Ret'First + 1) = ':' then return "/cygdrive/" & Ret (Ret'First) & Ret (Ret'First + 2 .. Ret'Last); else return Ret; end if; end; when FS_Unknown => raise Invalid_Filesystem; end case; end To_Unix; --------------- -- From_Unix -- --------------- function From_Unix (FS : FS_Type; Path : FS_String) return FS_String is begin case FS is when FS_Unix | FS_Unix_Case_Insensitive => return Path; when FS_Windows => declare Ret : FS_String := Path; begin for J in Ret'Range loop if Ret (J) = '/' then Ret (J) := '\'; end if; end loop; if Ret'Length >= 11 -- "/cygdrive/X"'Length and then Ret (Ret'First .. Ret'First + 9) = "\cygdrive\" then return Ret (Ret'First + 10) & ":" & Ret (Ret'First + 11 .. Ret'Last); else return Ret; end if; end; when FS_Unknown => raise Invalid_Filesystem; end case; end From_Unix; -------------------- -- File_Extension -- -------------------- function File_Extension (FS : FS_Type; Path : FS_String) return FS_String is begin for J in reverse Path'Range loop if Path (J) = '.' then case FS is when FS_Unix | FS_Unix_Case_Insensitive => return Path (J .. Path'Last); when FS_Windows => return FS_String (To_Lower (String (Path (J .. Path'Last)))); when FS_Unknown => raise Invalid_Filesystem; end case; end if; end loop; return ""; end File_Extension; --------------- -- Base_Name -- --------------- function Base_Name (FS : FS_Type; Path : FS_String; Suffix : FS_String := "") return FS_String is begin for J in reverse Path'Range loop if Path (J) = Dir_Separator (FS) then if Path'Last - J < Suffix'Length or else Path (Path'Last - Suffix'Length + 1 .. Path'Last) /= Suffix then return Path (J + 1 .. Path'Last); else return Path (J + 1 .. Path'Last - Suffix'Length); end if; end if; end loop; return Path; end Base_Name; ------------------- -- Base_Dir_Name -- ------------------- function Base_Dir_Name (FS : FS_Type; Path : FS_String) return FS_String is Root : constant FS_String := Get_Root (FS, Path); begin if Path = Root then return Path; elsif Path (Path'Last) = Dir_Separator (FS) then return Base_Name (FS, Path (Path'First .. Path'Last - 1)); else return Base_Name (FS, Path); end if; end Base_Dir_Name; -------------- -- Dir_Name -- -------------- function Dir_Name (FS : FS_Type; Path : FS_String) return FS_String is Root : constant FS_String := Get_Root (FS, Path); begin if Root'Length > Path'Length then return ""; end if; for J in reverse Path'Range loop if Path (J) = Dir_Separator (FS) then return Path (Path'First .. J); end if; end loop; return Path; end Dir_Name; ---------------- -- Get_Parent -- ---------------- function Get_Parent (FS : FS_Type; Path : FS_String) return FS_String is begin if not Is_Dir_Name (FS, Path) then return Dir_Name (FS, Path); else return Dir_Name (FS, Path (Path'First .. Path'Last - 1)); end if; end Get_Parent; ----------------- -- Is_Dir_Name -- ----------------- function Is_Dir_Name (FS : FS_Type; Path : FS_String) return Boolean is begin return Path'Length > 0 and then Path (Path'Last) = Dir_Separator (FS); end Is_Dir_Name; ---------------------- -- Ensure_Directory -- ---------------------- function Ensure_Directory (FS : FS_Type; Path : FS_String) return FS_String is begin if not Is_Dir_Name (FS, Path) then return Path & Dir_Separator (FS); else return Path; end if; end Ensure_Directory; ----------------- -- Device_Name -- ----------------- function Device_Name (FS : FS_Type; Path : FS_String) return FS_String is begin if FS = FS_Windows and then Path'Length > 2 and then Path (Path'First + 1) = ':' then return "" & Path (Path'First); else return ""; end if; end Device_Name; --------------- -- Normalize -- --------------- function Normalize (FS : FS_Type; Path : FS_String) return FS_String is Dest : FS_String := Path; Src : Natural := Path'First; Idx : Natural := Dest'First; Tmp : Natural; DS : Character renames Dir_Separator (FS); begin while Src <= Path'Last loop if Idx > Dest'First and then Dest (Idx - 1) = DS then if Path (Src) = '.' then -- Skip when the "directory" is just "." if Src >= Path'Last or else Path (Src + 1) = DS then Src := Src + 2; elsif Path (Src + 1) = '.' then -- The "directory" part is ".." (not followed by a name). -- Insert ".." if we are at beginning of string. if Src + 1 >= Path'Last or else Path (Src + 2) = DS then -- Need to remove the previous directory name, unless -- it itself was a ".." that could not be normalized -- because it was at the beginning of the string. Tmp := Idx - 2; while Tmp >= Dest'First and then Dest (Tmp) /= DS loop Tmp := Tmp - 1; end loop; if Dest (Tmp + 1 .. Idx - 2) /= ".." then Idx := Tmp + 1; Src := Src + 3; -- Skip "../" else Dest (Idx) := '.'; Dest (Idx + 1) := '.'; Idx := Idx + 2; Src := Src + 2; -- Skip ".." end if; -- A name that starts with ".." else Dest (Idx) := '.'; Dest (Idx + 1) := '.'; Idx := Idx + 2; Src := Src + 2; end if; else Dest (Idx) := Path (Src); Idx := Idx + 1; Src := Src + 1; end if; else Dest (Idx) := Path (Src); Idx := Idx + 1; Src := Src + 1; end if; else Dest (Idx) := Path (Src); Idx := Idx + 1; Src := Src + 1; end if; end loop; return Dest (Dest'First .. Idx - 1); end Normalize; ------------------- -- Relative_Path -- ------------------- function Relative_Path (FS : FS_Type; Ref : FS_String; Path : FS_String) return FS_String is Depth : Natural := 0; Last : Natural := Ref'Last; Old : Natural; function Depth_Image return FS_String; -- Return "../" * Depth ----------------- -- Depth_Image -- ----------------- function Depth_Image return FS_String is Ret : FS_String (1 .. 3 * Depth); Sep : constant FS_String := ".." & Dir_Separator (FS); begin for J in 1 .. Depth loop Ret (J * 3 - 2 .. J * 3) := Sep; end loop; return Ret; end Depth_Image; begin -- If roots are not identical, then this means that we need to return -- the file's full name. if not Equal (FS, Get_Root (FS, Ref), Get_Root (FS, Path)) then return Path; end if; if Equal (FS, Ref, Path) then return "."; end if; loop exit when Last - Ref'First + 1 <= Path'Length and then Equal (FS, Path (Path'First .. Path'First + Last - Ref'First), Ref (Ref'First .. Last)); Old := Last; for J in reverse Ref'First .. Last - 1 loop if Ref (J) = Dir_Separator (FS) then Depth := Depth + 1; Last := J; exit; end if; end loop; if Old = Last then -- No Dir_Separator in Ref... Let's return Path return Path; end if; end loop; return Depth_Image & Path (Path'First + Last - Ref'First + 1 .. Path'Last); end Relative_Path; end GNATCOLL.Path; gnatcoll-core-21.0.0/src/gnatcoll-io-remote-windows.ads0000644000175000017500000001163713661715457022665 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNAT.Strings; use GNAT.Strings; with GNATCOLL.Remote; use GNATCOLL.Remote; package GNATCOLL.IO.Remote.Windows is -- The following methods are equivalent to their native counterparts. -- See GNATCOLL.IO for documentation. function Current_Dir (Exec : access Server_Record'Class) return FS_String; function Home_Dir (Exec : access Server_Record'Class) return FS_String; function Tmp_Dir (Exec : access Server_Record'Class) return FS_String; function Get_Logical_Drives (Exec : access Server_Record'Class) return String_List_Access; function Locate_On_Path (Exec : access Server_Record'Class; Base : FS_String) return FS_String; function Is_Regular_File (Exec : access Server_Record'Class; File : FS_String) return Boolean; function Size (Exec : access Server_Record'Class; File : FS_String) return Long_Integer; function Is_Directory (Exec : access Server_Record'Class; File : FS_String) return Boolean; function Is_Symbolic_Link (Exec : access Server_Record'Class; File : FS_String) return Boolean; function File_Time_Stamp (Exec : access Server_Record'Class; File : FS_String) return Ada.Calendar.Time; function Is_Writable (Exec : access Server_Record'Class; File : FS_String) return Boolean; procedure Set_Writable (Exec : access Server_Record'Class; File : FS_String; State : Boolean); function Is_Readable (Exec : access Server_Record'Class; File : FS_String) return Boolean; procedure Set_Readable (Exec : access Server_Record'Class; File : FS_String; State : Boolean); procedure Rename (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean); procedure Copy (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean); procedure Delete (Exec : access Server_Record'Class; File : FS_String; Success : out Boolean); function Read_Whole_File (Exec : access Server_Record'Class; File : FS_String) return GNAT.Strings.String_Access; function Read_Whole_File (Exec : access Server_Record'Class; File : FS_String) return GNATCOLL.Strings.XString; function Write_File (Exec : access Server_Record'Class; File : FS_String; Content : String) return Boolean; function Change_Dir (Exec : access Server_Record'Class; Dir : FS_String) return Boolean; function Read_Dir (Exec : access Server_Record'Class; Dir : FS_String; Dirs_Only : Boolean := False; Files_Only : Boolean := False) return GNAT.Strings.String_List; function Make_Dir (Exec : access Server_Record'Class; Dir : FS_String; Recursive : Boolean) return Boolean; procedure Copy_Dir (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean); procedure Delete_Dir (Exec : access Server_Record'Class; Dir : FS_String; Recursive : Boolean; Success : out Boolean); end GNATCOLL.IO.Remote.Windows; gnatcoll-core-21.0.0/src/gnatcoll-string_builders.adb0000644000175000017500000001721013661715457022444 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Unchecked_Deallocation; package body GNATCOLL.String_Builders is Minimal_Heap_Size : constant Natural := 64; procedure Free is new Ada.Unchecked_Deallocation (String, String_Access); procedure Allocate (Self : in out String_Builder; Chars : Natural); -------------- -- Allocate -- -------------- procedure Allocate (Self : in out String_Builder; Chars : Natural) is Str_Max : Natural := (if Self.Heap_Str = null then Minimal_Heap_Size else Self.Heap_Str'Length); begin -- Ensure we have room for total length + 1 (for ASCII.NUL) while Self.Str_Last + Chars + 1 > Str_Max loop Str_Max := Str_Max * 2; end loop; -- Perform reallocations if Self.Heap_Str = null or else Str_Max > Self.Heap_Str'Length then declare New_Str : constant String_Access := new String (1 .. Str_Max); begin if Self.Heap_Str /= null then -- Copy previous content if necessary New_Str (1 .. Self.Str_Last + 1) := Self.Heap_Str (1 .. Self.Str_Last + 1); Free (Self.Heap_Str); elsif Self.Str_Last > 0 then New_Str (1 .. Self.Str_Last + 1) := Self.Stack_Str (1 .. Self.Str_Last + 1); end if; Self.Heap_Str := New_Str; end; end if; end Allocate; ------------ -- Append -- ------------ procedure Append (Self : in out String_Builder; Str : String) is New_Last : constant Natural := Self.Str_Last + Str'Length; begin if Str'Length = 0 then return; end if; if New_Last > String_Builder_Short_Size then Allocate (Self, Str'Length); Self.Heap_Str (Self.Str_Last + 1 .. New_Last) := Str; Self.Heap_Str (New_Last + 1) := ASCII.NUL; else Self.Stack_Str (Self.Str_Last + 1 .. New_Last) := Str; Self.Stack_Str (New_Last + 1) := ASCII.NUL; end if; Self.Str_Last := New_Last; end Append; procedure Append (Self : in out Static_String_Builder; Str : String) is New_Last : constant Natural := Self.Str_Last + Str'Length; begin if Str'Length = 0 then return; end if; if New_Last > Self.Size_With_NUL - 1 then raise Constraint_Error; end if; Self.Str (Self.Str_Last + 1 .. New_Last) := Str; Self.Str_Last := New_Last; Self.Str (Self.Str_Last + 1) := ASCII.NUL; end Append; procedure Append (Self : in out String_Builder; Char : Character) is begin if Self.Str_Last + 1 > String_Builder_Short_Size then Allocate (Self, 1); Self.Str_Last := Self.Str_Last + 1; Self.Heap_Str (Self.Str_Last) := Char; Self.Heap_Str (Self.Str_Last + 1) := ASCII.NUL; else Self.Str_Last := Self.Str_Last + 1; Self.Stack_Str (Self.Str_Last) := Char; Self.Stack_Str (Self.Str_Last + 1) := ASCII.NUL; end if; end Append; procedure Append (Self : in out Static_String_Builder; Char : Character) is New_Last : constant Natural := Self.Str_Last + 1; begin if New_Last > Self.Size_With_NUL - 1 then raise Constraint_Error; end if; Self.Str_Last := New_Last; Self.Str (Self.Str_Last) := Char; Self.Str (Self.Str_Last + 1) := ASCII.NUL; end Append; --------------- -- As_String -- --------------- function As_String (Self : String_Builder) return String is begin if Self.Str_Last > String_Builder_Short_Size then return Self.Heap_Str.all (1 .. Self.Str_Last); else return Self.Stack_Str (1 .. Self.Str_Last); end if; end As_String; function As_String (Self : Static_String_Builder) return String is begin return Self.Str (1 .. Self.Str_Last); end As_String; ---------------- -- As_CString -- ---------------- function As_CString (Self : String_Builder) return CString is begin if Self.Str_Last = 0 then return Empty_CString; elsif Self.Str_Last > String_Builder_Short_Size then return CString (Self.Heap_Str (1)'Address); else return CString (Self.Stack_Str (1)'Address); end if; end As_CString; function As_CString (Self : Static_String_Builder) return CString is begin if Self.Str_Last = 0 then return Empty_CString; else return CString (Self.Str (1)'Address); end if; end As_CString; ---------------- -- Deallocate -- ---------------- procedure Deallocate (Self : in out String_Builder) is begin if Self.Heap_Str /= null then Free (Self.Heap_Str); Self.Str_Last := 0; end if; end Deallocate; ------------- -- Element -- ------------- function Element (Self : String_Builder; N : Positive) return Character is begin if N > Self.Str_Last then raise Constraint_Error; elsif Self.Str_Last > String_Builder_Short_Size then return Self.Heap_Str.all (N); else return Self.Stack_Str (N); end if; end Element; function Element (Self : Static_String_Builder; N : Positive) return Character is begin if N > Self.Str_Last then raise Constraint_Error; else return Self.Str (N); end if; end Element; ------------ -- Length -- ------------ function Length (Self : String_Builder) return Natural is begin return Self.Str_Last; end Length; function Length (Self : Static_String_Builder) return Natural is begin return Self.Str_Last; end Length; --------- -- Set -- --------- procedure Set (Self : in out String_Builder; Str : String) is begin Self.Str_Last := 0; Append (Self, Str); end Set; procedure Set (Self : in out Static_String_Builder; Str : String) is begin Self.Str_Last := 0; Append (Self, Str); end Set; end GNATCOLL.String_Builders; gnatcoll-core-21.0.0/src/gnatcoll-coders-streams.adb0000644000175000017500000001701613661715457022204 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package body GNATCOLL.Coders.Streams is ---------- -- Read -- ---------- overriding procedure Read (Stream : in out Stream_Type; Item : out Stream_Element_Array; Last : out Stream_Element_Offset) is Buffer : constant access Stream_Element_Array := Stream.Buffer.Reference.Element; procedure Read_Stream (Item : out Stream_Element_Array; Last : out Stream_Element_Offset); ----------------- -- Read_Stream -- ----------------- procedure Read_Stream (Item : out Stream_Element_Array; Last : out Stream_Element_Offset) is begin Stream.Read_From.Read (Item, Last); if (Stream.Read_Ends = Partial_Read and then Last < Item'Last) or else (Stream.Read_Ends = Empty_Read and then Last < Item'First) then Stream.End_Of_Read := True; end if; end Read_Stream; procedure Read_Over is new Coders.Read (Read => Read_Stream, Buffer => Buffer.all, Rest_First => Stream.Rest_First, Rest_Last => Stream.Rest_Last); begin Read_Over (Stream.Read_Coder.all, Item, Last, Flush => (if Stream.End_Of_Read then Finish else No_Flush)); end Read; ------------------ -- End_Of_Input -- ------------------ procedure End_Of_Input (Stream : in out Stream_Type) is begin Stream.End_Of_Read := True; end End_Of_Input; function End_Of_Input (Stream : Stream_Type) return Boolean is begin return Stream.End_Of_Read; end End_Of_Input; ----------- -- Write -- ----------- overriding procedure Write (Stream : in out Stream_Type; Item : Stream_Element_Array) is procedure Write_Stream (Item : Stream_Element_Array); ----------- -- Write -- ----------- procedure Write_Stream (Item : Stream_Element_Array) is begin Stream.Write_To.Write (Item); end Write_Stream; procedure Write_Over is new Coders.Write (Write => Write_Stream, Buffer_Size => Stream.Buffer_Size); begin Write_Over (Stream.Write_Coder.all, Item, No_Flush); end Write; ----------- -- Flush -- ----------- procedure Flush (Stream : in out Stream_Type; Mode : Flush_Mode := Sync_Flush) is Buffer : Stream_Element_Array (1 .. Stream.Buffer_Size); Last : Stream_Element_Offset; begin loop Stream.Write_Coder.Flush (Buffer, Last, Mode); Stream.Write_To.Write (Buffer (1 .. Last)); exit when Last < Buffer'Last; end loop; end Flush; ---------------- -- Flush_Read -- ---------------- procedure Flush_Read (Stream : in out Stream_Type; Item : out Stream_Element_Array; Last : out Stream_Element_Offset; Mode : Flush_Mode := Sync_Flush) is begin Stream.Read_Coder.Flush (Item, Last, Mode); end Flush_Read; ------------------- -- Read_Total_In -- ------------------- function Read_Total_In (Stream : Stream_Type) return Stream_Element_Count is begin return (if Stream.Read_Coder = null then 0 else Stream.Read_Coder.Total_In); end Read_Total_In; -------------------- -- Read_Total_Out -- -------------------- function Read_Total_Out (Stream : Stream_Type) return Stream_Element_Count is begin return (if Stream.Read_Coder = null then 0 else Stream.Read_Coder.Total_Out); end Read_Total_Out; -------------------- -- Write_Total_In -- -------------------- function Write_Total_In (Stream : Stream_Type) return Stream_Element_Count is begin return (if Stream.Write_Coder = null then 0 else Stream.Write_Coder.Total_In); end Write_Total_In; --------------------- -- Write_Total_Out -- --------------------- function Write_Total_Out (Stream : Stream_Type) return Stream_Element_Count is begin return (if Stream.Write_Coder = null then 0 else Stream.Write_Coder.Total_Out); end Write_Total_Out; ---------------- -- Initialize -- ---------------- procedure Initialize (Stream : in out Stream_Type; Read_Coder : access Coder_Interface'Class := null; Write_Coder : access Coder_Interface'Class := null; Read_From : access Root_Stream_Type'Class := null; Write_To : access Root_Stream_Type'Class := null; Read_Ends_By : End_Of_Input_Method := Empty_Read; Read_Buffer_Size : Stream_Element_Count := Default_Buffer_Size; Write_Buffer_Size : Stream_Element_Count := Default_Buffer_Size) is begin if (Read_Coder = null) /= (Read_From = null) then raise Constraint_Error with "Read coder and stream have to be either null or not null together"; end if; if (Write_Coder = null) /= (Write_To = null) then raise Constraint_Error with "Write coder and stream have to be either null or not null" & " together"; end if; if Write_To = null and then Read_From = null then raise Constraint_Error with "Either write or read coders and streams have to be defined"; end if; Stream.Read_Coder := Read_Coder; Stream.Write_Coder := Write_Coder; Stream.Read_From := Read_From; Stream.Write_To := Write_To; Stream.Read_Ends := Read_Ends_By; Stream.End_Of_Read := False; if Read_Coder /= null then Stream.Buffer := SEA_Holders.To_Holder ((1 .. Read_Buffer_Size => 0)); Stream.Rest_First := Read_Buffer_Size + 1; Stream.Rest_Last := Read_Buffer_Size; end if; Stream.Buffer_Size := Write_Buffer_Size; end Initialize; end GNATCOLL.Coders.Streams; gnatcoll-core-21.0.0/src/gnatcoll-scripts.ads0000644000175000017500000021052113743647711020753 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This module provides various types and subprograms to integrate various -- external scripting languages. -- This API was designed so that multiple scripting languages can be used with -- your application, and so that the core of the application and all the -- various modules remain as independent as possible from the specific -- language. pragma Ada_2012; with Ada.Calendar; with Ada.Containers.Indefinite_Doubly_Linked_Lists; with Ada.Containers.Indefinite_Hashed_Maps; with Ada.Strings.Hash; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with GNAT.OS_Lib; with GNAT.Strings; with GNATCOLL.Arg_Lists; use GNATCOLL.Arg_Lists; with GNATCOLL.Refcount; use GNATCOLL.Refcount; with GNATCOLL.Utils; use GNATCOLL.Utils; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Any_Types; use GNATCOLL.Any_Types; with System; use System; package GNATCOLL.Scripts is type Scripts_Repository_Record is tagged private; type Scripts_Repository is access all Scripts_Repository_Record'Class; type Scripting_Language_Record is abstract tagged private; type Scripting_Language is access all Scripting_Language_Record'Class; type Cst_Argument_List is array (Natural range <>) of Cst_String_Access; type Callback_Data is abstract tagged private; type Callback_Data_Access is access all Callback_Data'Class; -- Data used to communicate with the scripting language engine, to marshal -- the parameters and return values. type Class_Instance is private; ---------------------- -- Subprogram types -- ---------------------- type Subprogram_Record is abstract tagged private; type Subprogram_Type is access all Subprogram_Record'Class; pragma No_Strict_Aliasing (Subprogram_Type); -- This type represents a subprogram for the language. In Python, this -- is a python object which is a function or method. -- Do not confuse this with a shell command, it has a more general meaning. -- In particular, the user cannot define new shell commands in the GPS -- shell, and thus Subprogram_Record has a broader meaning. procedure Free (Subprogram : in out Subprogram_Type); -- Free the subprogram function Get_Script (Subprogram : Subprogram_Record) return Scripting_Language is abstract; -- Return the language in which the subprogram was written procedure Free (Subprogram : in out Subprogram_Record) is abstract; -- Free the memory occupied by the subprogram instance function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return Boolean; function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return String; function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return Class_Instance; function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return Any_Type; function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return GNAT.Strings.String_List; function Execute (Subprogram : access Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return Boolean is abstract; function Execute (Subprogram : access Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return String is abstract; function Execute (Subprogram : access Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return Class_Instance is abstract; function Execute (Subprogram : access Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return Any_Type is abstract; function Execute (Subprogram : access Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return GNAT.Strings.String_List is abstract; -- Execute the subprogram with the given arguments, and return its output. -- Returned value must be freed by the caller. -- For a String_List, some items in the result value might be left to null -- if the corresponding element from the shell is not a string. function Get_Name (Subprogram : access Subprogram_Record) return String is abstract; -- Return the name of the subprogram, as a string that can be displayed for -- the user. This is used when analyzing the contents of a hook for -- instance ------------------ -- Module types -- ------------------ type Module_Type is private; Default_Module : constant Module_Type; -- A module is equivalent to an Ada package, or a namespace in C++. -- It is a way to group classes and subprograms into their own namespace. -- -- By default, all functions and classes are exported to the module defined -- in a module defined by the scripting language (for python, this default -- module is defined in GNATCOLL.Scripts.Python.Register_Python_Scripting). -- -- But it is possible to export to other modules instead function Lookup_Module (Repo : access Scripts_Repository_Record; Qualified_Name : String) return Module_Type; -- Lookup an existing module or create it if needed. -- The qualified name uses '.' as the separator, and all intermediate -- levels are created as needed. The name of the top-level module must be -- included, so even if you passed "MyApp" as the Module name to -- Register_Python_Scripting, the qualified name here should look like -- MyApp.Module1.Module2 -- In practice, the module might not be created until you actually add a -- class or a function to it. -- As a special case, Qualified_Name may start with "@." to indicate a -- submodule of the default module, which avoids duplicating the name of -- that default module in several places of the application. ----------------- -- Class types -- ----------------- type Class_Type is private; No_Class : constant Class_Type; -- A class type, which can be used to create new instances. Primitive -- operations (aka methods) can be associated with the class. This is the -- primary way to make new subprograms available to the user, while -- organizing them into namespaces. Any_Class : constant Class_Type; -- Constant that can be used in the call to Nth_Arg below to indicate -- that the nth parameter is an instance, but its actual class is -- undefined No_Class_Instance : constant Class_Instance; -- The instance of a class, which embeds some Ada data. This type is -- reference counted, and will automatically take care of memory management -- issues. function Lookup_Class (Repo : access Scripts_Repository_Record; Name : String; Module : Module_Type := Default_Module) return Class_Type; -- Return a Class_Type for Name. -- If the given class does not exist, a dummy version is created (but is -- not exported to the scripting languages). This is for instance -- convenient to represent one of the builtin classes for the languages, -- although it might be dangerous since not all languages have the same -- builtins. -- If you use a dummy version as a base class in New_Class, and it doesn't -- exist in the language, then this is equivalent to not having a base -- class. function New_Class (Repo : access Scripts_Repository_Record'Class; Name : String; Base : Class_Type := No_Class; Module : Module_Type := Default_Module) return Class_Type; -- For some languages, this notion is not supported, and the class will not -- be visible by the user in the shell. Methods created for the class will -- then simply be made available directly in the shell. -- If a class with the same name was created, it is returned, and no class -- is created anew. -- Base is the base class, or parent class. It only needs to be specified -- the first time the class is created (typically just before the matching -- calls to Register_Command), and can be left to its default value -- afterward. function Get_Name (Class : Class_Type) return String; -- Return the name of the class (module.name) ------------------- -- Callback_Data -- ------------------- Invalid_Parameter : exception; No_Such_Parameter : exception; function Create (Script : access Scripting_Language_Record; Arguments_Count : Natural) return Callback_Data'Class is abstract; -- Create a new empty list of arguments. You must call Set_Nth_Arg for -- each of these arguments before using the return value. function Command_Line_Treatment (Script : access Scripting_Language_Record) return Command_Line_Mode is abstract; -- Indicates how command lines should be treated by GPS. -- If the returned type is Separate_Args, then GPS should handle the -- parsing and separating of arguments. -- Otherwise GPS should just manipulate the command lines as raw strings. procedure Free (Data : in out Callback_Data) is abstract; procedure Free (Data : in out Callback_Data_Access); -- Free the memory occupied by Data. This needs to be called only if Data -- was created through Create function Clone (Data : Callback_Data) return Callback_Data'Class is abstract; -- Clone Data. The result value must be freed by the caller procedure Set_Nth_Arg (Data : in out Callback_Data; N : Positive; Value : String) is abstract; procedure Set_Nth_Arg (Data : in out Callback_Data; N : Positive; Value : Integer) is abstract; procedure Set_Nth_Arg (Data : in out Callback_Data; N : Positive; Value : Float) is abstract; procedure Set_Nth_Arg (Data : in out Callback_Data; N : Positive; Value : Boolean) is abstract; procedure Set_Nth_Arg (Data : in out Callback_Data; N : Positive; Value : Class_Instance) is abstract; procedure Set_Nth_Arg (Data : in out Callback_Data; N : Positive; Value : Subprogram_Type) is abstract; procedure Set_Nth_Arg (Data : in out Callback_Data'Class; N : Positive; Value : Filesystem_String); -- Set the nth argument of Data function Number_Of_Arguments (Data : Callback_Data) return Natural is abstract; -- Return the number of arguments passed to that callback. The number of -- arguments has already been check before the transfer to your own -- subprogram. procedure Name_Parameters (Data : in out Callback_Data; Names : Cst_Argument_List) is abstract; -- Name the parameters, for languages which support it. -- For instance, the following call: -- Name_Parameters (Data, (1 => new String'("a"), -- 2 => new String'("b"), -- 3 => new String'("c"))); -- will provide support for the following python calls: -- func (1, 2, 3) -- func (1, c=3, b=2) -- This call has no effect for languages which do not support name -- parameters. -- After calling this procedure, the parameters are reordered so that no -- matter what order the user specified them in, calling Nth_Arg (2) will -- always return the value for b. -- You should pass a default value to Nth_Arg, since otherwise if a -- parameter was not given on the command line, even if later parameters -- were given, Nth_Arg will raise Invalid_Parameter. -- -- It is recommended that Names be a global constant, which you can also -- use when registering the command, through Parameter_Names_To_Usage, so -- that the documentation remains up-to-date. -- -- Names should not include "self" in the case of methods. This is an -- implicit parameter in most languages. function Get_Script (Data : Callback_Data) return Scripting_Language is abstract; -- Return the scripting language that created Data function Get_Repository (Data : Callback_Data) return Scripts_Repository; -- Return the kernel associated with Data function Nth_Arg (Data : Callback_Data; N : Positive) return String is abstract; function Nth_Arg (Data : Callback_Data; N : Positive) return Unbounded_String is abstract; function Nth_Arg (Data : Callback_Data'Class; N : Positive) return Filesystem_String; function Nth_Arg (Data : Callback_Data; N : Positive) return Integer is abstract; function Nth_Arg (Data : Callback_Data; N : Positive) return Float is abstract; function Nth_Arg (Data : Callback_Data; N : Positive) return Boolean is abstract; -- Get the nth argument to the function, starting from 1. -- If there is not enough parameters, No_Such_Parameter is raised -- If the parameters doesn't have the right type, Invalid_Parameter is -- raised. function Nth_Arg (Data : Callback_Data; N : Positive) return Subprogram_Type is abstract; -- Same as above, for a subprogram. The returned value must be freed function Nth_Arg (Data : Callback_Data; N : Positive; Class : Class_Type := Any_Class; Allow_Null : Boolean := False) return Class_Instance is abstract; -- The class_instance must belong to Class or its children, or -- Invalid_Parameter is also raised. -- The return value must be freed by the caller. -- If Allow_Null is true, then a null instance might be passed as a -- parameter. If it is false, passing a null instance will raise -- Invalid_Parameter. -- Class can be set to Any_Class to indicate that the instance can be -- of any class. function Nth_Arg (Data : Callback_Data; N : Positive; Default : String) return String; function Nth_Arg (Data : Callback_Data; N : Positive; Default : Filesystem_String) return Filesystem_String; function Nth_Arg (Data : Callback_Data; N : Positive; Default : Integer) return Integer; function Nth_Arg (Data : Callback_Data; N : Positive; Default : Float) return Float; function Nth_Arg (Data : Callback_Data; N : Positive; Default : Boolean) return Boolean; function Nth_Arg (Data : Callback_Data; N : Positive; Class : Class_Type := Any_Class; Default : Class_Instance; Allow_Null : Boolean := False) return Class_Instance; function Nth_Arg (Data : Callback_Data; N : Positive; Default : Subprogram_Type) return Subprogram_Type; -- Same as above, except that if there are not enough parameters, Default -- is returned. Returned value must be freed. procedure Set_Error_Msg (Data : in out Callback_Data; Msg : String) is abstract; -- Set an error message. -- The return value for this callback will be ignored. On most languages -- (python,...) this is equivalent to raising an exception. -- If Msg is set to the empty string, an exception will still be raised procedure Set_Return_Value_As_List (Data : in out Callback_Data; Size : Natural := 0; Class : Class_Type := No_Class) is abstract; -- Setup the return value as an empty list. New values can be appended to -- the list with Set_Return_Value. -- It is possible to override the exact returned type by setting Class. -- This should however be a subclass of the builtin "list" for language -- in which it makes sense. This is often risky if one of the scripting -- languages your application cannot create subclasses of lists. -- If Size is not 0, then the list has a fixed size. Depending on the -- language, this could be a different type, such as a tuple in python. -- -- See also the documentation for List_Instance for a full example -- returning a list to the scripting language. procedure Set_Return_Value (Data : in out Callback_Data; Value : Integer) is abstract; procedure Set_Return_Value (Data : in out Callback_Data; Value : Float) is abstract; procedure Set_Return_Value (Data : in out Callback_Data; Value : String) is abstract; procedure Set_Return_Value (Data : in out Callback_Data; Value : Boolean) is abstract; procedure Set_Return_Value (Data : in out Callback_Data; Value : Class_Instance) is abstract; procedure Set_Return_Value (Data : in out Callback_Data'Class; Value : Filesystem_String); -- Set the return value of Data. -- If the return value was set as a list, Value is appended to the -- list. For languages that do not support lists, the append is only -- performed for strings (newline-separated). Other data types simply -- replace the current return value. procedure Set_Address_Return_Value (Data : in out Callback_Data; Value : System.Address) is abstract; -- Set the return value of Data to Value. The address will be represented -- as an integer on the python side, and a string in Shell. -- -- NOTE: This is a low level primitive, and is not meant to be used as-is, -- as there is no appropriate representation of an address object on the -- python side. Rather, this is meant to be used in tandem with ctypes: -- -- On the Ada side: -- -- Set_Address_Return_Value (Data, My_Integer'Address); -- -- On the python side: -- -- import ctypes -- int_ptr = ctypes.POINTER(ctypes.int) -- -- # This is the result of the above Set_Address_Return_Value -- ada_address = AdaClass.ada_exposed_function() -- -- # We then convert it to a ctypes pointer -- c_int = ctypes.cast(int_ptr, ada_address) -- -- WARNING: This is a low level primitive dealing with memory, and as such, -- it is unsafe ! Make sure that the life time of the object you pass -- corresponds to the way it is used on the python side procedure Set_Return_Value_Key (Data : in out Callback_Data; Key : String; Append : Boolean := False) is abstract; procedure Set_Return_Value_Key (Data : in out Callback_Data; Key : Integer; Append : Boolean := False) is abstract; procedure Set_Return_Value_Key (Data : in out Callback_Data; Key : Class_Instance; Append : Boolean := False) is abstract; -- Move the current value of Data, as set by Set_Return_Value into a -- htable. -- Typical usage would be: -- Set_Return_Value (Data, 12); -- Set_Return_Value_Key (Data, "key1"); -- -- Set_Return_Value_As_List (Data); -- Set_Return_Value (Data, 1); -- Set_Return_Value (Data, 2); -- Set_Return_Value_Key (Data, "key2"); -- will create a htable containing (key1 => 12, key2 => (1, 2)) -- -- If Append is true and there is already a value set for Key, then the new -- value is append to it (a list is created if necessary). This might not -- be supported for languages that do not explicitly support htables like -- the GPS shell. -- -- No provision is made for creating htables of htables, although htables -- of lists are supported, or for getting the currently set value for Key. function Return_Value (Data : Callback_Data) return String is abstract; function Return_Value (Data : Callback_Data) return Integer is abstract; function Return_Value (Data : Callback_Data) return Float is abstract; function Return_Value (Data : Callback_Data) return Boolean is abstract; function Return_Value (Data : Callback_Data) return Class_Instance is abstract; -- Return the value returned by a script function, via a call to -- Execute_Command below. -- If the type you are requesting is not compatible with the actual -- returned value, Invalid_Parameter is raised. -- See also Return_Value below, which returns a List_Instance'Class. ----------- -- Lists -- ----------- subtype List_Instance is Callback_Data'Class; -- Represents a list passed as parameter. -- In the context of a list, Set_Nth_Arg will always append to the list if -- the given index is outside of the current range of the list. -- -- To return a list to the scripting language, you can therefore do the -- following: -- -- procedure Handler (Data : in out Callback_Data'Class; Cmd : String) is -- List : List_Instance := New_List (Get_Script (Data)); -- begin -- Set_Nth_Arg (List, Natural'Last, 12); -- Set_Nth_Arg (List, Natural'Last, "value"); -- Set_Return_Value (Data, List); -- end; -- -- The handling of the list can be made transparent by using the following -- construct: -- -- procedure Handler (Data : in out Callback_Data'Class; Cmd : String) is -- begin -- Set_Return_Value_As_List (Data); -- Set_Return_Value (Data, 12); -- Set_Return_Value (Data, "value"); -- end; -- -- However, this second approach does not let you return lists of list, -- for instance, which is doable with the first approach. function New_List (Script : access Scripting_Language_Record; Class : Class_Type := No_Class) return List_Instance'Class is abstract; -- Creates a new empty list -- It is possible to override the exact returned type by setting Class. -- This should however be a subclass of the builtin "list" for language -- in which it makes sense. This is often risky if one of the scripting -- languages your application cannot create subclasses of lists. function Nth_Arg (Data : Callback_Data; N : Positive) return List_Instance'Class is abstract; -- Get a list parameter. The default value is always the empty list, but -- you can still get an Invalid_Parameter exception if the corresponding -- parameter is not a list. -- In the case of python, this function will accept any iterable type (a -- list, a tuple, a user-defined type with a __iter__ method, even a -- dictionary or a string). function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return List_Instance; function Execute (Subprogram : access Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return List_Instance'Class is abstract; -- Execute a subprogram and assumes it returns a list. -- The resulting List must be freed by the caller. function Return_Value (Data : Callback_Data) return List_Instance'Class is abstract; -- Returns the list returned by a command (see Execute_Command). procedure Set_Nth_Arg (Data : in out Callback_Data; N : Positive; Value : List_Instance) is abstract; -- Override the nth arg in Data procedure Set_Return_Value (Data : in out Callback_Data; Value : List_Instance) is abstract; -- Set the value returned to the shell ------------------ -- Dictionaries -- ------------------ type Dictionary_Instance is abstract tagged null record; type Dictionary_Iterator is abstract tagged null record; function Nth_Arg (Data : Callback_Data; N : Positive) return Dictionary_Instance'Class is abstract; -- Get a dictionary parameter. The default value is always the empty -- dictionary, but you can still get an Invalid_Parameter exception if the -- corresponding parameter is not a list. function Iterator (Self : Dictionary_Instance) return Dictionary_Iterator'Class is abstract; -- Returns an iterator for the given dictionary. The returned iterator -- doesn't point to any pair in dictionary until the first call to Next function Has_Key (Self : Dictionary_Instance; Key : String) return Boolean is abstract; function Has_Key (Self : Dictionary_Instance; Key : Integer) return Boolean is abstract; function Has_Key (Self : Dictionary_Instance; Key : Float) return Boolean is abstract; function Has_Key (Self : Dictionary_Instance; Key : Boolean) return Boolean is abstract; -- Returns True when dictionary has value for given key function Value (Self : Dictionary_Instance; Key : String) return String is abstract; function Value (Self : Dictionary_Instance; Key : Integer) return String is abstract; function Value (Self : Dictionary_Instance; Key : Float) return String is abstract; function Value (Self : Dictionary_Instance; Key : Boolean) return String is abstract; function Value (Self : Dictionary_Instance; Key : String) return Integer is abstract; function Value (Self : Dictionary_Instance; Key : Integer) return Integer is abstract; function Value (Self : Dictionary_Instance; Key : Float) return Integer is abstract; function Value (Self : Dictionary_Instance; Key : Boolean) return Integer is abstract; function Value (Self : Dictionary_Instance; Key : String) return Float is abstract; function Value (Self : Dictionary_Instance; Key : Integer) return Float is abstract; function Value (Self : Dictionary_Instance; Key : Float) return Float is abstract; function Value (Self : Dictionary_Instance; Key : Boolean) return Float is abstract; function Value (Self : Dictionary_Instance; Key : String) return Boolean is abstract; function Value (Self : Dictionary_Instance; Key : Integer) return Boolean is abstract; function Value (Self : Dictionary_Instance; Key : Float) return Boolean is abstract; function Value (Self : Dictionary_Instance; Key : Boolean) return Boolean is abstract; -- Returns value of given key function Next (Self : not null access Dictionary_Iterator) return Boolean is abstract; -- Moves iterator to the next pair in dictionary. Returns False when there -- are no more pairs available. This allows to minimize code to iterator -- over dictionaries: -- -- declare -- Iter : aliased Dictionary_Iterator'Class := Dict.Iterator; -- begin -- while Next (Iter) loop -- ... -- end loop; -- end; function Key (Self : Dictionary_Iterator) return String is abstract; function Key (Self : Dictionary_Iterator) return Integer is abstract; function Key (Self : Dictionary_Iterator) return Float is abstract; function Key (Self : Dictionary_Iterator) return Boolean is abstract; -- Returns value of current pair in dictionary function Value (Self : Dictionary_Iterator) return String is abstract; function Value (Self : Dictionary_Iterator) return Integer is abstract; function Value (Self : Dictionary_Iterator) return Float is abstract; function Value (Self : Dictionary_Iterator) return Boolean is abstract; -- Returns value of current pair in dictionary --------------------- -- Class instances -- --------------------- Invalid_Data : exception; function New_Instance (Script : access Scripting_Language_Record; Class : Class_Type) return Class_Instance is abstract; -- Create a new instance of the class. -- No data is stored in the object. -- This call should generally be the result of the user calling a -- function, which acts as a constructor for the class. -- The instance constructor (Constructor_Method) is not called, even -- though the instance has been properly initialized. You should therefore -- perform any initialization manually just after calling New_Instance. function Get_Method (Instance : Class_Instance; Name : String) return Subprogram_Type; -- Return the method of instance Instance. Returned value must be freed by -- the caller. -- Parameters passed to the return value must not specify the instance as -- first parameter. function Is_Subclass (Instance : Class_Instance; Base : Class_Type) return Boolean; function Is_Subclass (Instance : Class_Instance; Base : String) return Boolean; -- Whether Instance is a Base or from a subclass of Base function Get_Script (Instance : Class_Instance) return Scripting_Language; -- Return the scripting language that created this instance function Get_Data (Instance : Class_Instance; Name : Class_Type) return Integer; function Get_Data (Instance : Class_Instance; Name : Class_Type) return Float; function Get_Data (Instance : Class_Instance; Name : Class_Type) return String; function Get_Data (Instance : Class_Instance; Name : Class_Type) return Boolean; -- Get the data embedded in the class. -- These are specialized cases of Get_Data below. -- Invalid_Data is raised if no such data was stored in the instance. -- Constraint_Error is raised if the data is not of the appropriate type. -- Class is used to differentiate the data for instances that inherit from -- several GPS classes, as in: -- class Foo (GPS.Console, GPS.Process): -- def __init__ (self): -- GPS.Console.__init__ (self,..) -- GPS.Process.__init__ (self,...) -- since both internal classes expect different data stored internally procedure Unset_Data (Instance : Class_Instance; Name : Class_Type); procedure Unset_Data (Instance : Class_Instance; Name : String); -- Unset all data stored for the given name procedure Set_Data (Instance : Class_Instance; Name : Class_Type; Value : String); procedure Set_Data (Instance : Class_Instance; Name : Class_Type; Value : Integer); procedure Set_Data (Instance : Class_Instance; Name : Class_Type; Value : Float); procedure Set_Data (Instance : Class_Instance; Name : Class_Type; Value : Boolean); -- Associate some data with the instance. -- These are specialized cases of Set_Data below. -- The class name is required to handle multiple inheritance: if we were -- always using the same internal identifier to associated data with the -- instance, then we couldn't have a class with multiple ancestors, each -- expecting its own user data set in the constructor. procedure Set_Property (Instance : Class_Instance; Name : String; Value : Integer); procedure Set_Property (Instance : Class_Instance; Name : String; Value : Float); procedure Set_Property (Instance : Class_Instance; Name : String; Value : String); procedure Set_Property (Instance : Class_Instance; Name : String; Value : Boolean); -- Export a field stored in the instance. -- The way to access it depends on the language: -- - in the GPS shell, you need to prefix its name with "@", as in: -- > Console "foo" # Create new instance -- > @id %1 # Access its "id" property -- - in Python, this is used with the usual python conventions: -- > c = Console ("foo") -- > c.id -- The value of the field can be overridden in the scripting language, but -- this change will not be reflected in Ada. For instance, in python: -- c.id = 2 -- is valid, but will have no effect on the Ada side. -- -- If you want true read-only properties, you need to use Register_Property -- through getters and setters. -- -- In Python, this procedure doesn't go through the class's __setattr_ -- function. -------------------- -- Instance lists -- -------------------- -- Most internal objects, when exported to a shell, should reuse the same -- class instance whenever the same physical object is referenced. This is -- so that the user can store user data within the instance, and get it -- back easily the next time the same object is referenced. -- For types derived from GObject_Record, we provide appropriate Set_Data -- and Get_Data subprograms. For other types, the instance_list type can -- be used to store the instances (of which there is one per scripting -- language). type Instance_List is private; Null_Instance_List : constant Instance_List; -- Stores the instance created for some GPS internal data, so that the same -- script instance is reused every time we reference the same Ada object. type Inst_Cursor is private; function First (Self : Instance_List) return Inst_Cursor; procedure Next (Self : Instance_List; Pos : in out Inst_Cursor); function Has_Element (Position : Inst_Cursor) return Boolean; function Element (Self : Instance_List; Pos : Inst_Cursor) return Class_Instance; -- Iterate on the list of instances stored in a list. Only valid -- instances are returned (never a No_Class_Instance) procedure Free (List : in out Instance_List); -- Free the instances stored in the list function Get (List : Instance_List; Script : access Scripting_Language_Record'Class) return Class_Instance; -- Return the instance for a given script procedure Set (List : in out Instance_List; Inst : Class_Instance); -- Set the instance for a specific language ------------------------- -- Instance properties -- ------------------------- type Instance_Property_Record is abstract tagged null record; type Instance_Property is access all Instance_Property_Record'Class; procedure Destroy (Prop : in out Instance_Property_Record); -- Type of data that can be associated with a class_instance. This is a -- general type, but simpler types are provided already function Create_Property (Val : Boolean) return Instance_Property_Record'Class; function Create_Property (Val : Integer) return Instance_Property_Record'Class; function Create_Property (Val : Float) return Instance_Property_Record'Class; function Create_Property (Val : String) return Instance_Property_Record'Class; -- Return an instance of Instance_Property that wraps one of the basic -- types. The returned value must be Destroyed, unless you store it -- through Set_Data, in which case GNATCOLL will take care of that. function As_Boolean (Prop : Instance_Property_Record'Class) return Boolean; function As_Integer (Prop : Instance_Property_Record'Class) return Integer; function As_Float (Prop : Instance_Property_Record'Class) return Float; function As_String (Prop : Instance_Property_Record'Class) return String; -- Assuming Prop was created with Create_Property, return its value procedure Set_Data (Instance : Class_Instance; Name : String; Property : Instance_Property_Record'Class); -- Associate user data with Instance. Multiple data can be stored in a -- given instance, each associated with a different Name. Typically, GPS -- classes use the class name as the property name to avoid conflicts. -- When the property is no longer needed (either because it is replaced by -- another one with the same name, or because Instance is destroyed), the -- Destroy operation is called on Property. -- Note that a copy of Property is stored, not Property itself. -- -- A simplified interface for some scalar types is also defined, see -- Set_Data above function Get_Data (Instance : Class_Instance; Name : String) return Instance_Property; -- Return a general property associated with the widget. -- Return null if there is no such property. --------------------------- -- Class_Instance_Record -- --------------------------- -- This type encapsulate some language specific data. It is overridden by -- each of the scripting languages. Do not use directly unless you are -- implementing a new scripting language type Class_Instance_Record is abstract tagged private; type Class_Instance_Record_Access is access all Class_Instance_Record'Class; -- A type overridden by each of the scripting languages function Is_Subclass (Instance : access Class_Instance_Record; Base : String) return Boolean is abstract; -- Whether Instance is a Base or from a subclass of Base. Do not use -- directly, use the version that takes a Class_Instance instead function Get_CIR (Inst : Class_Instance) return Class_Instance_Record_Access; -- For internal use only function Get_Method (Inst : access Class_Instance_Record; Name : String) return Subprogram_Type is abstract; function Print_Refcount (Instance : access Class_Instance_Record) return String; -- Debug only: print the reference counting for this instance. -- Implementations are encourage to concatenate with the inherited -- method's result procedure Set_Property (Instance : access Class_Instance_Record; Name : String; Value : Integer) is abstract; procedure Set_Property (Instance : access Class_Instance_Record; Name : String; Value : Float) is abstract; procedure Set_Property (Instance : access Class_Instance_Record; Name : String; Value : Boolean) is abstract; procedure Set_Property (Instance : access Class_Instance_Record; Name : String; Value : String) is abstract; -- See definition of Set_Constant (Class_Instance) procedure Set_Data (Instance : access Class_Instance_Record'Class; Name : String; Property : Instance_Property_Record'Class); function Get_Data (Instance : access Class_Instance_Record'Class; Name : String) return Instance_Property; -- Internal version of Set_Data/Get_Data. -- For internal use only ------------------------- -- Callback_Data lists -- ------------------------- -- This type's goal is similar to the one for the instance lists, since the -- callback_data are also language-specific type Callback_Data_List is private; -- Stores a list of callback_data, each associated with a different -- scripting language procedure Free (List : in out Callback_Data_List); -- Free the instances stored in the list function Get (Repo : access Scripts_Repository_Record'Class; List : Callback_Data_List; Script : access Scripting_Language_Record'Class) return Callback_Data_Access; -- Return the data for a given script. -- The returned value should not be freed by the caller, it is the -- responsibility of the callback_data_list to do so. procedure Set (Repo : access Scripts_Repository_Record'Class; List : in out Callback_Data_List; Script : access Scripting_Language_Record'Class; Data : Callback_Data_Access); -- Set the data for a specific language. Data should not be freed by the -- caller. --------------- -- Consoles -- --------------- -- When executing script commands, they will very often produce some -- output, including possibly error or log messages. The following class -- acts as a small wrapper around more advanced types of console, like a -- text-mode console, or a GtkAda console. This type is used so that the -- subprograms below can be used both in graphical and textual mode type Virtual_Console_Record is abstract tagged private; type Virtual_Console is access all Virtual_Console_Record'Class; procedure Insert_Text (Console : access Virtual_Console_Record; Txt : String) is abstract; -- Prints some output in the console procedure Insert_Log (Console : access Virtual_Console_Record; Txt : String) is null; pragma Obsolescent (Insert_Log); -- ignored, kept for backward compatibility only procedure Insert_Error (Console : access Virtual_Console_Record; Txt : String) is abstract; -- Prints an error message resulting from the wrong execution of a script procedure Insert_Prompt (Console : access Virtual_Console_Record; Txt : String) is abstract; -- Display Txt as a new prompt in the console procedure Ref (Console : access Virtual_Console_Record) is null; procedure Unref (Console : access Virtual_Console_Record) is null; -- Increment or decrement the reference counting for the console, if that -- notion makes sense for that particular console. -- The idea is that when we are temporary using a different console for the -- output, we do not want the default console to be destroyed -- automatically, in case its only reference was hold by the scripting -- language. procedure Grab_Events (Console : access Virtual_Console_Record; Grab : Boolean) is null; -- Make sure all graphical events go to the console instead of the rest of -- the application. -- This is mostly used to avoid recursive re-entrant calls to the script -- interpreter. procedure Set_As_Default_Console (Console : access Virtual_Console_Record; Script : Scripting_Language := null) is null; -- Called when Console becomes the default console for the scripting -- language Script. -- Script might be null when the Console is no longer the default console -- for that script. procedure Set_Data_Primitive (Instance : Class_Instance; Console : access Virtual_Console_Record) is abstract; function Get_Instance (Script : access Scripting_Language_Record'Class; Console : access Virtual_Console_Record) return Class_Instance is abstract; -- Associate a console and class instances, so that a given instance is -- always associated with the same class instance. -- Typical example of implementation would be: -- type My_Console is new Virtual_Console_Record with record -- Instances : Instance_List; -- end record; -- -- procedure Set_Data_Primitive (...) is -- begin -- Set (Console.Instances, Get_Script (Instance), Instance); -- end Set_Data_Primitive; -- -- function Get_Instance (...) is -- begin -- return Get (Console.Instances, Script); -- end Get_Instance; procedure Set_Data (Instance : Class_Instance; Console : access Virtual_Console_Record'Class); function Get_Data (Instance : Class_Instance) return Virtual_Console; -- Return the virtual console stored in Instance procedure Process_Pending_Events_Primitive (Console : access Virtual_Console_Record) is null; procedure Process_Pending_Events (Console : access Virtual_Console_Record'Class); -- Process all pending graphical events, so that the application is -- properly refreshed while a script is running. -- This package will properly make sure this function is not called too -- often, so you don't need to do additional work for that procedure Clear (Console : access Virtual_Console_Record) is null; -- Clear the contents of the console function Read (Console : access Virtual_Console_Record; Size : Integer; Whole_Line : Boolean; Prompt : String) return String; function Read (Console : access Virtual_Console_Record; Size : Integer; Whole_Line : Boolean) return String; -- Return at most Size characters from the console. -- If Whole_Line is true, the returned value stops at the first newline -- character seen in any case. -- If Prompt is specified, it is displayed first (via Insert_Prompt). ------------------------- -- Scripting languages -- ------------------------- type Module_Command_Function is access procedure (Data : in out Callback_Data'Class; Command : String); -- The callback handler for a command. -- The first argument is always the instance to which the method applies, -- if Command is a method. -- Should raise Invalid_Parameters if one of the parameters is incorrect. -- The number of parameters has been checked before this procedure is -- called. procedure Destroy (Script : access Scripting_Language_Record) is null; -- Destroy the scripting language and the memory it occupies type Param_Descr is private; type Param_Array is array (Natural range <>) of Param_Descr; type Param_Array_Access is access all Param_Array; No_Params : constant Param_Array; -- Description of a parameter function Param (Name : String; Optional : Boolean := False) return Param_Descr; -- Describe one of the parameters of a script function type Command_Descr; type Command_Descr_Access is access all Command_Descr; type Command_Descr (Length : Natural) is record Command : String (1 .. Length); Handler : Module_Command_Function; Class : Class_Type := No_Class; Params : Param_Array_Access; Static_Method : Boolean := False; Minimum_Args : Natural := 0; Maximum_Args : Natural := 0; Next : Command_Descr_Access; end record; -- Params is left to null if the user did not specify the name of -- parameters in the call to Register_Command (this is different from -- having a non-null but empty Params, which indicates there are no -- parameters). procedure Register_Command (Script : access Scripting_Language_Record; Command : Command_Descr_Access) is abstract; -- Register a new callback for a command. -- Command will exist as long as Script, so it is safe (and recommended) -- that script points to Command instead of duplicating the data. This -- saves memory by sharing storage among all the scripting languages. -- See also Register_Command applied to the script_repository for more -- information. type Property_Descr; type Property_Descr_Access is access all Property_Descr; type Property_Descr (Length : Natural) is record Name : String (1 .. Length); Class : Class_Type; Setter : Module_Command_Function; Getter : Module_Command_Function; Next : Property_Descr_Access; end record; -- The setter passes two parameters: first one is the instance, second one -- is the value of the property. Note that the property is untyped: you -- might have to try the various Nth_Arg to find out which type the user -- has passed. You can use Set_Error_Message if the property does not have -- the expected type. -- -- The getter passes one parameter in Callback, which is the instance on -- which the property applies. -- It should call Set_Return_Value to return the value of the property. -- -- You can potentially use the same callback in both cases, and count the -- number of arguments to find out whether the user is querying or setting -- the property. procedure Register_Property (Script : access Scripting_Language_Record; Prop : Property_Descr_Access) is abstract; -- See documentation of Register_Property applied on the Scripts_Repository procedure Register_Class (Script : access Scripting_Language_Record; Name : String; Base : Class_Type := No_Class; Module : Module_Type := Default_Module) is abstract; -- Create a new class in the interpreter. -- This is a low-level procedure, use New_Class instead procedure Block_Commands (Script : access Scripting_Language_Record; Block : Boolean) is abstract; -- If Block is true, no command can be executed for this scripting language procedure Set_Default_Console (Script : access Scripting_Language_Record; Console : Virtual_Console); -- Defines the console to use to display output, when none is specified -- to Execute_Command below function Get_Default_Console (Script : access Scripting_Language_Record) return Virtual_Console; -- Return the default console used for all outputs by this scripting -- language procedure Display_Prompt (Script : access Scripting_Language_Record; Console : Virtual_Console := null) is null; -- Display the prompt on the script's default console. It uses -- Display_Prompt to compute the prompt to display. function Get_Prompt (Script : access Scripting_Language_Record) return String is abstract; -- Return the prompt to display procedure Execute_Command (Script : access Scripting_Language_Record; CL : Arg_List; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : out Boolean) is abstract; -- Execute a command in the script language. -- It isn't possible to retrieve the result of that command, this command -- is only used for its side effect. -- Depending on the language, Command might be a list of commands to -- execute, often semicolon or newline separated. -- Errors is set to True if there was any error executing the script. -- -- The result of the command, as well as the text of the command itself, -- are not visible to the user if Hide_Output is True. Otherwise, the text -- is sent to Console. Any output done by the command, however (via "print" -- or "sys.stdout.write" statements for instance in python) will be -- displayed. -- -- If Show_Command is True and Hide_Output is False, then the command -- itself is also printed in the console function Execute_Command (Script : access Scripting_Language_Record; CL : Arg_List; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : access Boolean) return String; -- Execute a command, and return its output as a displayable string. -- Note: some languages might simply return an empty string if they cannot -- capture the output of their interpreter. This command is mostly useful -- for the GPS shell, but also supported by python. -- Command can never be a list of commands (no semicolon or newline -- separated). function Execute_Command (Script : access Scripting_Language_Record; CL : Arg_List; Console : Virtual_Console := null; Hide_Output : Boolean := False; Errors : access Boolean) return Boolean is abstract; -- Execute a command and evaluate its return value (*not* its output) as a -- boolean. This is different from the version returning a string, in that -- only the return value is considered, not the full output. procedure Execute_Command (Script : access Scripting_Language_Record; Command : String; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : out Boolean); function Execute_Command (Script : access Scripting_Language_Record; Command : String; Console : Virtual_Console := null; Hide_Output : Boolean := False; Errors : access Boolean) return Boolean; function Execute_Command (Script : Scripting_Language; Command : String; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : access Boolean) return String; -- Same as above, working directly on a String. This String is interpreted -- as a command line using the mechanism described in -- GNATCOLL.Command_Lines. -- These are only provided for backward compatibility and you should use -- directly the version that takes a Command_Line whenever possible. function Execute_Command (Script : access Scripting_Language_Record; Command : String; Args : Callback_Data'Class) return Boolean is abstract; -- Execute a command, the argument of which are specified separately in -- Args. -- Return the value returned by the command itself. Error_In_Command : exception; procedure Execute_Expression (Result : in out Callback_Data; Expression : String; Hide_Output : Boolean := True) is abstract; -- Execute any expression, and store the result in Result. -- Resulted must have been Created, all its arguments are ignored. -- It must be freed by the caller. procedure Execute_Command (Args : in out Callback_Data; Command : String; Hide_Output : Boolean := True) is abstract; -- Execute the given function passing one or more arguments via Args. -- On exit, Args is modified to contain the value returned by the command. -- If you know the expected result type, you can then use the Return_Value -- functions above to retrieve the values. -- declare -- C : Callback_Data'Class := Create (Script, 1); -- begin -- Set_Nth_Arg (C, 1, "some value"); -- Execute_Command (C, "somefunction"); -- Put_Line (Return_Value (C)); -- If returned a string -- Put_Line (Integer'Image (Return_Value (C))); -- If an integer -- -- declare -- L : List_Instance'Class := Return_Value (C); -- If a list -- begin -- for Item in 1 .. Number_Of_Arguments (L) loop -- Put_Line (Nth_Arg (L, Item)); -- A list of strings ? -- end loop; -- end; -- end; -- -- If the command returns an error (or raised an exception), an Ada -- exception is raised in turn (Error_In_Command). The exception is also -- printed on the current console for the language. -- -- This procedure expects Command to be the name of a function. To -- execute any expression, see Execute_Expression instead function Execute_Command_With_Args (Script : access Scripting_Language_Record; CL : Arg_List) return String; -- Execute a command. -- This procedure needs only be implemented for the GPS shell, in all other -- language you should keep the default which raises Program_Error, since -- this function is not used anywhere but for shell commands. -- All output is hidden procedure Execute_File (Script : access Scripting_Language_Record; Filename : String; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : out Boolean) is abstract; -- Execute a script contained in an external file type Script_Loader is access function (File : GNATCOLL.VFS.Virtual_File) return Boolean; function Load_All (File : GNATCOLL.VFS.Virtual_File) return Boolean; -- Given the name of a script, returns True if the script should be loaded procedure Load_Directory (Script : access Scripting_Language_Record; Directory : GNATCOLL.VFS.Virtual_File; To_Load : Script_Loader := Load_All'Access) is null; -- Load all scripts found in the given directory, and for which To_Load -- returns True. function Interrupt (Script : access Scripting_Language_Record) return Boolean; -- Interrupt the command currently executed. -- The interrupt need not be synchronous, but should occur as soon as -- possible. -- Returns True if the execution could be interrupt, False if there is no -- command being executed, or it can't be interrupted package String_Lists is new Ada.Containers.Indefinite_Doubly_Linked_Lists (String); package String_Lists_Sort is new String_Lists.Generic_Sorting; procedure Complete (Script : access Scripting_Language_Record; Input : String; Completions : out String_Lists.List); -- Provide the list of possible completion when the user has typed Input in -- a console. This completion can be as smart as possible, but can also -- return an empty list if that scripting language doesn't support -- completion. function Get_Name (Script : access Scripting_Language_Record) return String is abstract; -- The name of the scripting language function Get_Repository (Script : access Scripting_Language_Record) return Scripts_Repository is abstract; -- Return the kernel in which Script is registered function Current_Script (Script : access Scripting_Language_Record) return String is abstract; -- Return the name of the current script (file or inline script) that we -- are executing. When unknown, the empty string should be returned. -------------------------- -- Commands and methods -- -------------------------- Constructor_Method : constant String; Addition_Method : constant String; Substraction_Method : constant String; Destructor_Method : constant String; Comparison_Method : constant String; -- Should return -1, 0 or 1 depending on whether AB Equal_Method : constant String; -- Should return a boolean, testing for equality. -- Note that at least in python, defining this will not automatically -- define the inequality, so it might be better to use Comparison_Method -- instead. procedure Destroy (Repo : in out Scripts_Repository); -- Free all memory associated with the repository procedure Register_Standard_Classes (Repo : access Scripts_Repository_Record'Class; Console_Class_Name : String; Logger_Class_Name : String := ""); -- Register predefined classes that are needed for support of consoles. -- If Logger_Class_Name, this also creates a new class to interface with -- the GNATCOLL.Traces mechanism. This is especially useful if your own -- application is also uses the same mechanism. function Get_Console_Class (Repo : access Scripts_Repository_Record'Class) return Class_Type; -- Return the class to use for Console input/output. -- This is only initialized when Register_Standard_Classes is called procedure Register_Command (Repo : access Scripts_Repository_Record'Class; Command : String; Params : Param_Array; Handler : Module_Command_Function; Class : Class_Type := No_Class; Static_Method : Boolean := False; Language : String := ""); procedure Register_Command (Repo : access Scripts_Repository_Record'Class; Command : String; Minimum_Args : Natural := 0; Maximum_Args : Natural := 0; Handler : Module_Command_Function; Class : Class_Type := No_Class; Static_Method : Boolean := False; Language : String := ""); -- Add a new function to all currently registered script languages. -- -- The first version is recommended. By contrast, you will need to call -- Name_Parameters yourself in the Handler for the second version. -- -- Params should not be freed by the caller. -- -- If Class is not No_Class, then this procedure creates a method for this -- class, for the languages for which this is appropriate. An extra -- parameter is automatically added to the command, in first position, -- which is the instance to which this applies. In some shells, the user -- must provide this himself (GPS shell for instance), since the language -- is not object oriented. This first parameter must not be counted in -- Minimum_args and Maximum_Args -- Otherwise, it creates a global function in the script language. -- -- If Static_Method is True, then Class must be different from No_Class. -- The resulting method doesn't take an instance as its first -- parameter. Instead, it behaves like a global function, except it is in a -- specific namespace corresponding to the class name. -- This is similar to C++'s static methods. -- -- If Command is Constructor_Method, then the function is setup as the -- constructor for Class, which must not be No_Class. For compatibility -- with the greater number of languages, only one such constructor can be -- defined per class. -- A constructor receives an already built instance of the object, and -- should initialize the fields. Its first parameter is the instance, the -- second, third,... are the parameters passed to the constructor. -- The constructor shouldn't return any value through Set_Return_Value. -- -- If Command is Addition_Method, this is a function that should take one -- argument in addition to the instance, and return a new instance. This -- handles statements like "inst + 1", although the second argument can be -- of any type (you can even handle multiple types in your implementation) -- -- Subscription_Method is similar to Addition_Method. -- -- Comparison_Method is a function that takes a second parameter, and -- returns -1 if the first is less than the second, 0 if they are equal, -- and 1 if the first is greater than the second. -- -- Destructor_Method is called just before the instance is destroyed -- -- If the command has some graphical output (dialog,...), it must run in -- a separate main loop (Gtk.Main.Gtk_Main or modal dialogs). -- -- Language can be specified to restrict the command to a specific -- scripting language. procedure Override_Command (Repo : access Scripts_Repository_Record'Class; Command : String; Handler : Module_Command_Function; Class : Class_Type := No_Class); -- You can change behavior of already registered function providing -- new Handler for it. See Register_Command for parameter descriptions. procedure Register_Property (Repo : access Scripts_Repository_Record'Class; Name : String; Class : Class_Type; Setter : Module_Command_Function := null; Getter : Module_Command_Function := null); -- Defines a property which is accessed through methods. -- If Setter is null, the property is read-only. -- If Getter is null, the property is write-only. -- -- A property is very similar to two functions, but the syntax might be -- different. For instance: -- - In python: -- c = Console() # Create instance -- c.msg = "message" # Calls the setter -- print c.msg # Calls the getter -- A function would have been: -- c.set_msg("message") -- print c.get_msg() -- -- - In shell: -- Console # create instance -- @msg %1 "message" # Calls the setter -- @msg %2 # Calls the getter -- A function would have been: -- Console.set_msg %1 "message" -- Console.get_msg %2 procedure Block_Commands (Repo : access Scripts_Repository_Record'Class; Block : Boolean); -- Block all execution of shell commands if Block is true procedure Register_Scripting_Language (Repo : access Scripts_Repository_Record'Class; Script : access Scripting_Language_Record'Class); -- Register a new scripting language in the kernel. -- Scripting languages are freed when the kernel is destroyed function Lookup_Scripting_Language (Repo : access Scripts_Repository_Record'Class; Name : String) return Scripting_Language; -- Lookup one of the registered languages by name type Scripting_Language_Array is array (Natural range <>) of Scripting_Language; function Get_Scripting_Languages (Repo : access Scripts_Repository_Record'Class) return Scripting_Language_Array; -- Return the list of all registered languages No_Args : constant GNAT.OS_Lib.Argument_List := (1 .. 0 => null); private Constructor_Method : constant String := "<@constructor@>"; Addition_Method : constant String := "+"; Substraction_Method : constant String := "-"; Comparison_Method : constant String := "<=>"; Destructor_Method : constant String := "<@destructor@>"; Equal_Method : constant String := "=="; type Virtual_Console_Record is abstract tagged record Hide_Output : Boolean := False; Refresh_Timeout : Ada.Calendar.Time := Ada.Calendar.Clock; end record; type Class_Type is record Qualified_Name : GNAT.Strings.String_Access; -- Fully qualified name for the class (module.module.name) Exists : Boolean := True; -- Set to False when the class is found using Lookup_Class. This is for -- instance the case for builtin classes. end record; type Module_Type is record Name : Ada.Strings.Unbounded.Unbounded_String; end record; Default_Module : constant Module_Type := (Name => Ada.Strings.Unbounded.To_Unbounded_String ("@")); type User_Data; type User_Data_List is access User_Data; type User_Data (Length : Natural) is record Next : User_Data_List; Name : String (1 .. Length); Prop : Instance_Property; end record; procedure Free_User_Data_List (Data : in out User_Data_List); -- Free the whole contents of the list type Param_Descr is record Name : GNAT.Strings.String_Access; Optional : Boolean := False; end record; No_Params : constant Param_Array := (1 .. 0 => <>); type Class_Instance_Record is abstract new Refcounted with record Script : access Scripting_Language_Record'Class; -- not owned end record; function Get_User_Data (Self : not null access Class_Instance_Record) return access User_Data_List; -- Return the list of user data stored for this instance. Depending on the -- scripting language, this list might be stored in various places (as a -- python attribute, directly in Ada for the shell,...) This list is shared -- amongst the scripting languages. package CI_Pointers is new Smart_Pointers (Class_Instance_Record); type Class_Instance is record Ref : CI_Pointers.Ref; end record; -- A Class_Instance cannot be a visibly tagged type if declared in this -- package, since otherwise we have operations dispatching on multiple -- types. No_Class_Instance : constant Class_Instance := (Ref => CI_Pointers.Null_Ref); No_Class : constant Class_Type := (Qualified_Name => null, Exists => False); Any_Class : constant Class_Type := (Qualified_Name => new String'("@#!-"), Exists => False); type Subprogram_Record is abstract tagged null record; type Callback_Data is abstract tagged null record; type Scripting_Language_Record is abstract tagged record Console : Virtual_Console; end record; type Instance_Array is array (Natural range <>) of Class_Instance; type Instance_Array_Access is access Instance_Array; type Instance_List is record List : Instance_Array_Access; -- instances are stored in no particular order. As soon as a -- No_Class_Instance is found, there will be no further instances -- in the array. end record; Null_Instance_List : constant Instance_List := (List => null); type Inst_Cursor is record Index : Natural := Natural'Last; end record; type Callback_Data_Array is array (Natural range <>) of Callback_Data_Access; type Callback_Data_List is access Callback_Data_Array; type Scripting_Language_List is access Scripting_Language_Array; package Classes_Hash is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => String, Element_Type => Class_Type, Hash => Ada.Strings.Hash, Equivalent_Keys => "="); type Scripts_Repository_Record is tagged record Scripting_Languages : Scripting_Language_List := new Scripting_Language_Array'(1 .. 0 => null); Commands : Command_Descr_Access; Properties : Property_Descr_Access; Classes : Classes_Hash.Map; Console_Class : Class_Type := No_Class; Logger_Class : Class_Type := No_Class; end record; end GNATCOLL.Scripts; gnatcoll-core-21.0.0/src/gnatcoll-tribooleans.ads0000644000175000017500000001233313661715457021610 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package GNATCOLL.Tribooleans is type Triboolean is (True, False, Indeterminate); -- This type is an extension to the basic boolean type. -- It provides a 3-state boolean logic, where the first two states are -- equivalent to the standard Boolean values. -- You can easily provide a renaming for the Indeterminate state by -- declaring a constant: -- Maybe : constant Triboolean := Indeterminate; function To_TriBoolean (Value : Boolean) return Triboolean; -- Convert a boolean into a TriBoolean function To_Boolean (Value : Triboolean) return Boolean; -- Convert to a boolean, with the following rules: -- if Value is True, the resulting boolean is true, otherwise the result -- is false. function "not" (Value : Triboolean) return Triboolean; -- Returns the negative of a triboolean: -- True => False -- False => True, -- Indeterminate => Indeterminate function "and" (Value1, Value2 : Triboolean) return Triboolean; function "and" (Value1 : Triboolean; Value2 : Boolean) return Triboolean; function "and" (Value1 : Boolean; Value2 : Triboolean) return Triboolean; -- Logical "and" between two tribooleans, with the following truth table: -- T | F | I -- ---------- -- T | T | F | I -- F | F | F | F -- I | I | F | I function "or" (Value1, Value2 : Triboolean) return Triboolean; function "or" (Value1 : Triboolean; Value2 : Boolean) return Triboolean; function "or" (Value1 : Boolean; Value2 : Triboolean) return Triboolean; -- Logical "or" between two tribooleans, with the following truth table: -- T | F | I -- ---------- -- T | T | T | T -- F | T | F | I -- I | T | I | I function "xor" (Value1, Value2 : Triboolean) return Triboolean; function "xor" (Value1 : Triboolean; Value2 : Boolean) return Triboolean; function "xor" (Value1 : Boolean; Value2 : Triboolean) return Triboolean; -- Logical "xor" between two tribooleans, with the following truth table: -- T | F | I -- ---------- -- T | F | T | I -- F | T | F | I -- I | I | I | I function "=" (Value1 : Boolean; Value2 : Triboolean) return Boolean; function "=" (Value1 : Triboolean; Value2 : Boolean) return Boolean; -- Compare a triboolean and a boolean. If the triboolean is Indeterminate, -- the result is always False. function Equal (Value1 : Triboolean; Value2 : Boolean) return Triboolean; function Equal (Value1 : Boolean; Value2 : Triboolean) return Triboolean; function Equal (Value1 : Triboolean; Value2 : Triboolean) return Triboolean; -- Compare two tribooleans, with the following truth table: -- T | F | I -- ---------- -- T | T | F | I -- F | F | T | I -- I | I | I | I -- Note that comparing two indeterminate values also returns indeterminate, -- as opposed to what "=" would return! function Image (Value : Triboolean) return String; function Value (Str : String) return Triboolean; -- Convert to and from a string. Any value that does not match the value -- that would be returned by Boolean'Image or Boolean'Value is declared as -- indeterminate. pragma Inline (To_TriBoolean); pragma Inline (To_Boolean); pragma Inline ("and"); pragma Inline ("or"); pragma Inline ("xor"); pragma Inline ("="); pragma Inline (Equal); end GNATCOLL.Tribooleans; gnatcoll-core-21.0.0/src/gnatcoll-refcount-weakref.adb0000644000175000017500000001070313743647711022512 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Atomic; use GNATCOLL.Atomic; package body GNATCOLL.Refcount.Weakref is use Proxy_Pointers; ---------- -- Free -- ---------- overriding procedure Free (Self : in out Weak_Refcounted) is begin if Self.Proxy /= Proxy_Pointers.Null_Ref then Proxy (Get (Self.Proxy).all).Proxied := null; Self.Proxy := Proxy_Pointers.Null_Ref; end if; Free (Refcounted (Self)); -- ??? static call to a "null" procedure end Free; ---------------------- -- Weakref_Pointers -- ---------------------- package body Weakref_Pointers is use Pointers; ------------------ -- Get_Weak_Ref -- ------------------ function Get_Weak_Ref (Self : Ref'Class) return Weak_Ref is Data : constant Encapsulated_Access := Self.Get; P : Proxy_Pointers.Ref; D : Proxy_Pointers.Encapsulated_Access; begin if Data = null then return Null_Weak_Ref; end if; P := GNATCOLL.Refcount.Weakref.Weak_Refcounted'Class (Data.all).Proxy; if P = Proxy_Pointers.Null_Ref then D := new Proxy'(GNATCOLL.Refcount.Refcounted with Proxied => Refcounted_Access (Data)); Set (P, D); -- now owns a reference to D Weak_Refcounted'Class (Data.all).Proxy := P; end if; return Weak_Ref (P); end Get_Weak_Ref; --------------- -- Was_Freed -- --------------- function Was_Freed (Self : Weak_Ref'Class) return Boolean is P : constant access Proxy := Proxy_Pointers.Get (Proxy_Pointers.Ref (Self)); begin return P = null or else P.Proxied = null; end Was_Freed; --------- -- Get -- --------- procedure Get (Self : Weak_Ref'Class; R : out Ref'Class) is P : constant access Proxy := Proxy_Pointers.Get (Proxy_Pointers.Ref (Self)); begin if Was_Freed (Self) then R.Set (null); else -- A subtetly here: it is possible that the element is actually -- being freed, and Free() is calling Get on one of the weakref. -- In such a case, we do not want to resuscitate the element if P.Proxied.Refcount = 0 then R.Set (null); else -- Adds a reference to P.Proxied R.Set (Encapsulated_Access (P.Proxied)); end if; end if; end Get; --------- -- Get -- --------- function Get (Self : Weak_Ref'Class) return Ref is Result : Ref; begin Get (Self, Result); return Result; end Get; end Weakref_Pointers; end GNATCOLL.Refcount.Weakref; gnatcoll-core-21.0.0/src/gnatcoll-refcount.ads0000644000175000017500000004342513661715457021122 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides support for reference counting. A Smart_Pointer -- plays the role of an access type (although it is not an access type), and -- keeps a reference to the designated entity. When a smart pointer goes out -- of scope, the designated entity's reference count is automatically -- decremented. -- When the reference count reaches 0, the corresponding entity is freed. -- -- This package also provides support for weak pointers. These do not prevent -- the freeing of the object they point to. However, when that object is -- freed, the weak pointer is safely reset to null. -- -- Cycles of references will prevent the freeing of the memory, since the -- objects' refcounts will never reach 0. For instance, if you consider a -- tree, the parent could hold a reference (via a smart_pointer) to each of -- its children. Thus the children will exist for at least as long as their -- parents. However, if the children also point to their parents with a -- smart_pointer, the parent can never be freed in the first place. The -- solution is that the children should point to their parents through a weak -- pointer instead. -- -- This package provides two versions of such pointers: -- * Smart_Pointers is the older version (obsolescent). It is less -- efficient, and the element_type must derive from the Refcounted -- type. -- * Shared_Pointers is a more flexible API, where the element_type -- can be any unconstrained type. The implementation is also faster. pragma Ada_2012; private with Ada.Finalization; with System; with GNATCOLL.Atomic; with GNATCOLL.Storage_Pools.Headers; use GNATCOLL.Storage_Pools.Headers; pragma Warnings (Off, "* is an internal GNAT unit"); with System.Soft_Links; use System.Soft_Links; pragma Warnings (On, "* is an internal GNAT unit"); package GNATCOLL.Refcount is ------------------- -- Internal data -- ------------------- -- This section provides several types that are used in the implementation -- of this package. They are not useful for applications. type Weak_Data is record Element : System.Address := System.Null_Address; Refcount : aliased Atomic.Atomic_Counter; Lock : aliased Atomic.Atomic_Counter; -- To resolve the race condition between the last Ref disappearing and -- the new Ref creation from Weak_Ref. end record; type Weak_Data_Access is access all Weak_Data; type Counters is record Refcount : aliased Atomic.Atomic_Counter := 1; Weak_Data : aliased Weak_Data_Access; -- A pointer to the weak pointer's data. This data is created the -- first time we create a weak pointer. We hold a reference to that -- data, so that it can never be freed while at least one reference -- exists. end record; type Counters_Access is access all Counters; package Headers is new Header_Pools (Counters, Counters_Access); Application_Uses_Tasks : constant Boolean := System.Soft_Links.Lock_Task /= System.Soft_Links.Task_Lock_NT'Access; -- Whether the tasking run time has been initialized. --------------------- -- Shared_Pointers -- --------------------- generic type Element_Type (<>) is private; -- The element that will be encapsulated within a smart pointer. -- We need to be able to copy it as part of Set. with procedure Release (Self : in out Element_Type) is null; -- This procedure should be used if you need to perform actions when -- the last reference to an element is removed. Typically, this is -- used to free element_type and its contents, when it is not a -- controlled type. Atomic_Counters : Boolean := Application_Uses_Tasks; -- Whether to use atomic (and thus thread-safe) counters. If set to -- True, the smart pointer is task safe. Of course, that does not -- mean that the Element_Type itself is task safe. -- This has a small impact on performance. package Shared_Pointers is pragma Suppress (All_Checks); Is_Task_Safe : constant Boolean := Atomic_Counters; -- Make the formal parameter visible to users of this package type Ref is tagged private; Null_Ref : constant Ref; -- This type acts like a pointer, but holds a reference to the object, -- which will thus never be freed while there exists at least one -- reference to it. type Weak_Ref is tagged private; Null_Weak_Ref : constant Weak_Ref; -- A weak reference to an object. The value returned by Get will be -- reset to null when the object is freed (because its last reference -- expired). Holding a weak reference does not prevent the deallocation -- of the object. package Pools is new Headers.Typed (Element_Type); subtype Element_Access is Pools.Element_Access; procedure Set (Self : in out Ref'Class; Data : Element_Type); pragma Inline (Set); -- A copy of Data will be put under control of Self, and freed when -- the last reference to it is removed. procedure From_Element (Self : out Ref'Class; Element : Element_Access); pragma Inline (From_Element); -- Given an element that is already under control of a -- shared pointer, returns the corresponding shared pointer. -- This is especially useful when the element_type is a tagged -- type. This element might be used for dynamic dispatching, but -- it might be necessary to retrieve the smart pointer: -- -- type Object is tagged private; -- package Pointers is new Shared_Pointers (Object'Class); -- use Pointers; -- -- procedure Method (Self : Object'Class) is -- R : Ref; -- begin -- From_Element (R, Self); -- end Method; -- -- R : Ref; -- R.Set (Obj); -- Method (R.Get); -- -- Warning: this must only be called when Element comes from a -- shared pointer, otherwise an invalid memory access will result. type Reference_Type (Element : access Element_Type) is limited null record with Implicit_Dereference => Element; -- A reference to an element_type. -- This type is used as the return value for Get, instead of an -- Element_Access, because it is safer: -- * applications cannot free the returned value (and -- they should never do it !) -- * the Element discriminant cannot be stored in a variable, -- so that prevents keeping a reference when it could be freed at -- any time. -- * since the type is limited, it is in general difficult to -- store it in records. This is intended, since the shared -- pointer itself should be stored instead (at the access type -- might be freed at any time). -- This type is often mostly transparent for the application. Assuming -- the Element_Type is defined as: -- -- type Element_Type is tagged record -- Field : Integer; -- end record; -- procedure Primitive (Self : Element_Type); -- procedure Primitive2 (Self : access Element_Type); -- -- then a shared pointer SP can be used as: -- -- SP.Get.Field := 1; -- SP.Get.Primitive1; -- SP.Get.Element.Primitive2; -- -- WARNING: -- The use of a reference_type ensures that Get can return an access to -- the object (more efficient than a copy when the objects are large), -- while preventing users from freeing the returned value. But this -- does not prevent all invalid cases. Using 'renames', for instance, -- can lead to invalid code, as in: -- -- package IP is new Shared_Pointers (Integer); -- use IP; -- R : Ref; -- R.Set (99); -- declare -- Int : Integer renames R.Get.Element.all; -- begin -- R := Null_Ref; -- Frees Int ! -- Put_Line (I'Img); -- Invalid memory access -- end; -- -- Another dangerous use is to have a procedure that receives the -- result of Get and modifies the shared pointer, as in: -- -- package OP is new Shared_Pointers (Object'Class); -- use OP; -- R : Ref; -- procedure Foo (Obj : Object'Class) is -- begin -- R := Null_Ref; -- freezes Obj ! -- end Foo; -- Foo (R.Get); -- -- The proper solution here is that Foo should receive the smart -- pointer itself, not the encapsulated value. function Unchecked_Get (Self : Ref'Class) return Element_Access with Inline; -- A version that returns directly the element access. This is meant -- for easy conversion of existing code, but its use is discouraged -- in new code, where Get should be used instead. -- The resulting access must not be deallocated. Passing it to -- Set might also be dangerous if the Element_Type contains data -- that might be freed when other smart pointers are freed. -- It also must not be stored in a record (store Self instead). function Get (Self : Ref'Class) return Reference_Type is ((Element => Unchecked_Get (Self))) with Inline; -- A safer version of Unchecked_Get. -- There is no performance penalty, since the compiler knows that a -- Reference_Type is in fact always of the same size and can be -- returned on the stack. -- It is safer because the associated access type cannot be converted -- to a non-local access type, nor freed. procedure Process (Self : Ref'Class; Process : not null access procedure (E : Element_Type)) with Inline; -- This procedure is similar to the function Get, but doesn't expose -- the access type to the user. -- This is safer than Get, since it avoids the multiple issues -- highlighted in the comments for Reference_Type (namely that Self -- might become null while the application holds a reference, which -- then references invalid memory). -- On the other hand, it is more awkward to use, and does not work if -- you need to pass multiple smart pointers. There is however nothing -- tricky in this procedure, since it simply calls -- Process (Self.Get) -- and the simple fact that Self is a parameter ensures it retains at -- least one reference during the execution of Process. -- -- If you want to always be on the safe side and prevent users from -- using Get, you could add the following configuration pragma to your -- compilation: -- pragma Restrictions -- (No_Use_Of_Entity => GNATCOLL.Refcount.Shared_Pointers.Get); function Is_Null (Self : Ref'Class) return Boolean with Inline; -- Whether the data is unset. Using this function might avoid the -- need for a "use type Element_Access" in your code. overriding function "=" (P1, P2 : Ref) return Boolean with Inline; -- This operator checks whether P1 and P2 share the same pointer. -- When the pointers differ, this operator returns False even if the -- two pointed elements are equal. function Weak (Self : Ref'Class) return Weak_Ref; procedure Set (Self : in out Ref'Class; Weak : Weak_Ref'Class); -- Set returns a reference to the object. Otherwise, it would be -- possible for a procedure to retrieve a pointer from the weak -- reference, and then reference it throughout the procedure, even -- though the pointer might be freed in between. -- -- If Weak is Null_Weak_Ref, then the element pointed by Self simply -- loses a reference, and Self points to nothing on exit. function Was_Freed (Self : Weak_Ref'Class) return Boolean; -- True if the object referenced by Self was freed. function Get_Refcount (Self : Ref'Class) return Natural; -- Return the current reference count. -- This is mostly intended for debug purposes. private type Ref is new Ada.Finalization.Controlled with record Data : Element_Access; end record; pragma Finalize_Storage_Only (Ref); overriding procedure Adjust (Self : in out Ref); pragma Inline (Adjust); overriding procedure Finalize (Self : in out Ref); type Weak_Ref is new Ada.Finalization.Controlled with record Data : Weak_Data_Access; end record; pragma Finalize_Storage_Only (Weak_Ref); overriding procedure Adjust (Self : in out Weak_Ref); pragma Inline (Adjust); overriding procedure Finalize (Self : in out Weak_Ref); Null_Ref : constant Ref := (Ada.Finalization.Controlled with Data => null); Null_Weak_Ref : constant Weak_Ref := (Ada.Finalization.Controlled with Data => null); end Shared_Pointers; -------------------- -- Smart_Pointers -- -------------------- -- For backward compatibility only. The above package is more flexible -- and more efficient. type Refcounted is abstract tagged private; type Refcounted_Access is access all Refcounted'Class; -- The common ancestor for all refcounted types. -- This ancestor adds a refcount field, which keeps track of how many -- references exist to a particular instance of Refcounted. -- -- The refcounting is task safe (that is you can use the smart pointer from -- multiple tasks concurrently, and the refcounting will always be -- accurate). But the task-safety of Refcounted itself depends on your -- application. procedure Free (Self : in out Refcounted) is null; -- Free the memory associated with Self, when Self is no longer referenced. generic type Encapsulated is abstract new Refcounted with private; package Smart_Pointers is pragma Obsolescent (Smart_Pointers, "Use Shared_Pointers instead"); type Encapsulated_Access is access all Encapsulated'Class; type Ref is tagged private; Null_Ref : constant Ref; procedure Set (Self : in out Ref; Data : Encapsulated'Class); procedure Set (Self : in out Ref; Data : access Encapsulated'Class); -- Replace the current contents of Self. -- Data is adopted by the smart pointer, and should no longer be -- referenced directly elsewhere. The reference count of Data is -- incremented by 1. -- Typical code looks like: -- Tmp := new Encapsulated; -- Set (Ptr, Tmp); -- (You can't do -- Set (Ptr, new Encapsulated); -- for visibility reasons) function Get (P : Ref) return Encapsulated_Access; pragma Inline (Get); -- Return a pointer the data pointed to by P. -- We return an access type for efficiency reasons. However, the -- returned value must not be freed by the caller. overriding function "=" (P1, P2 : Ref) return Boolean; -- Whether the two pointers point to the same data function Get_Refcount (Self : Ref) return Natural; -- Return the current reference count. -- This is mostly intended for debug purposes. private type Ref is new Ada.Finalization.Controlled with record Data : Refcounted_Access; end record; overriding procedure Finalize (P : in out Ref); overriding procedure Adjust (P : in out Ref); -- Take care of reference counting Null_Ref : constant Ref := (Ada.Finalization.Controlled with Data => null); end Smart_Pointers; private type Refcounted is abstract tagged record Refcount : aliased Atomic.Atomic_Counter := 0; end record; -- This requires, as a result, that all refcounted types also be tagged -- types (thus adding the size of a tag and the size of an integer to each -- instance). This approach was chosen over storing the refcounting -- independently of the refcounted type. The chosen approach provides a -- tighter integration between the two. end GNATCOLL.Refcount; gnatcoll-core-21.0.0/src/gnatcoll-scripts-files.adb0000644000175000017500000003257213661715457022044 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.VFS_Utils; use GNATCOLL.VFS_Utils; with GNATCOLL.Scripts.Projects; use GNATCOLL.Scripts.Projects; package body GNATCOLL.Scripts.Files is type File_Properties_Record is new Instance_Property_Record with record File : Virtual_File; end record; procedure File_Command_Handler (Data : in out Callback_Data'Class; Command : String); -- Handler for the "File" commands File_Class_Name : constant String := "File"; Default_Cst : aliased constant String := "default_to_root"; Name_Cst : aliased constant String := "name"; Local_Cst : aliased constant String := "local"; Server_Cst : aliased constant String := "remote_server"; File_Cmd_Parameters : constant Cst_Argument_List := (1 => Name_Cst'Access, 2 => Local_Cst'Access); File_Name_Parameters : constant Cst_Argument_List := (1 => Server_Cst'Access); File_Project_Parameters : constant Cst_Argument_List := (1 => Default_Cst'Access); ----------------- -- Create_File -- ----------------- function Create_File (Script : access Scripting_Language_Record'Class; File : GNATCOLL.VFS.Virtual_File) return Class_Instance is Instance : constant Class_Instance := New_Instance (Script, New_Class (Get_Repository (Script), File_Class_Name)); begin Set_Data (Instance, File); return Instance; end Create_File; -------------- -- Get_Data -- -------------- function Get_Data (Instance : Class_Instance) return GNATCOLL.VFS.Virtual_File is Data : Instance_Property; begin if Instance /= No_Class_Instance then Data := Get_Data (Instance, File_Class_Name); end if; if Data = null then return GNATCOLL.VFS.No_File; else return File_Properties_Record (Data.all).File; end if; end Get_Data; -------------------- -- Get_File_Class -- -------------------- function Get_File_Class (Data : Callback_Data'Class) return Class_Type is begin return New_Class (Data.Get_Repository, File_Class_Name); end Get_File_Class; -------------------------- -- File_Command_Handler -- -------------------------- procedure File_Command_Handler (Data : in out Callback_Data'Class; Command : String) is use type GNATCOLL.Projects.Project_Type; use type GNATCOLL.Projects.Project_Tree_Access; Info : Virtual_File; P : GNATCOLL.Projects.Project_Tree_Access; function Get_Project (File : Virtual_File; Default_To_Root : Boolean := False) return GNATCOLL.Projects.Project_Type; -- Return the project to which File belongs. If File does not belong to -- any project, return the root project if Default_To_Root is True. ----------------- -- Get_Project -- ----------------- function Get_Project (File : Virtual_File; Default_To_Root : Boolean := False) return GNATCOLL.Projects.Project_Type is File_Info : constant GNATCOLL.Projects.File_Info'Class := GNATCOLL.Projects.File_Info'Class (P.Info_Set (File).First_Element); Project : GNATCOLL.Projects.Project_Type; begin -- Return the first possible project, we have nothing else to base -- our guess on. Project := File_Info.Project; if Project = GNATCOLL.Projects.No_Project and then Default_To_Root then Project := P.Root_Project; end if; return Project; end Get_Project; begin if Command = Constructor_Method then Name_Parameters (Data, File_Cmd_Parameters); declare Instance : constant Class_Instance := Nth_Arg (Data, 1, Get_File_Class (Data)); Name : constant Filesystem_String := Nth_Arg (Data, 2); begin if Is_Absolute_Path (Name) then Set_Data (Instance, Create (Name)); return; end if; -- Base name case. Find full name using the following rules: -- 1) If third argument is set to true, create from current dir -- else -- 2) If Base Name can be found in project, use it -- else -- 3) Create from current dir -- If we really want to create from current directory if Number_Of_Arguments (Data) > 2 then declare From_Current : constant Boolean := Nth_Arg (Data, 3); begin if From_Current then Set_Data (Instance, Create_From_Dir (Get_Current_Dir, Name)); return; end if; end; end if; if Project_Tree /= null then declare File : constant Virtual_File := Project_Tree.Create (Base_Name (Create_From_Base (Name))); begin if File /= No_File then Set_Data (Instance, File); return; end if; end; end if; Set_Data (Instance, Create_From_Base (Name)); end; elsif Command = "name" then Name_Parameters (Data, File_Name_Parameters); Info := Nth_Arg (Data, 1); -- Ignore server argument here Set_Return_Value (Data, Full_Name (Info)); elsif Command = "path" then Info := Nth_Arg (Data, 1); Data.Set_Return_Value (Full_Name (Info)); elsif Command = "directory" then Info := Nth_Arg (Data, 1); Set_Return_Value (Data, Dir_Name (Info)); elsif Command = "other_file" then P := GNATCOLL.Scripts.Projects.Project_Tree; if P = null then Set_Error_Msg (Data, "Project not set"); else Info := Nth_Arg (Data, 1); Data.Set_Return_Value (Create_File (Data.Get_Script, P.Other_File (Info))); end if; elsif Command = "unit" then P := GNATCOLL.Scripts.Projects.Project_Tree; if P = null then Set_Error_Msg (Data, "Project not set"); return; end if; Info := Nth_Arg (Data, 1); -- Return the first possible project, we have nothing else to base -- our guess on. declare F_Info : constant GNATCOLL.Projects.File_Info'Class := GNATCOLL.Projects.File_Info'Class (P.Info_Set (Info).First_Element); begin Data.Set_Return_Value (F_Info.Unit_Name); end; elsif Command = "project" then P := GNATCOLL.Scripts.Projects.Project_Tree; if P = null then Set_Error_Msg (Data, "Project not set"); return; end if; Name_Parameters (Data, File_Project_Parameters); Info := Nth_Arg (Data, 1); Data.Set_Return_Value (GNATCOLL.Scripts.Projects.Create_Project (Data.Get_Script, Get_Project (Info, Default_To_Root => Nth_Arg (Data, 2, True)))); elsif Command = "executable_path" then P := GNATCOLL.Scripts.Projects.Project_Tree; if P = null then Set_Error_Msg (Data, "Project not set"); return; end if; Info := Nth_Arg (Data, 1); declare use GNATCOLL.Projects; Project : constant GNATCOLL.Projects.Project_Type := Get_Project (Info, Default_To_Root => True); Is_Native : constant Boolean := Project.Get_Target = "native" or else Project.Get_Target = ""; Include_Suffix : constant Boolean := Is_Native or else Project.Attribute_Value (Attribute => Build ("Builder", "Executable_Suffix")) /= ""; begin -- Don't include the suffix for non-native targets, unless if the -- Builder'Executable_Suffix has been explicitly set in the -- project. -- This is a temporary workaround for Q304-013. Data.Set_Return_Value (Create_File (Script => Data.Get_Script, File => Project.Executables_Directory / Project.Executable_Name (Info.Base_Name, Include_Suffix => Include_Suffix))); end; end if; end File_Command_Handler; -------------------- -- Get_File_Class -- -------------------- function Get_File_Class (Repo : access Scripts_Repository_Record'Class) return Class_Type is begin return New_Class (Repo, File_Class_Name); end Get_File_Class; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Callback_Data'Class; N : Positive) return GNATCOLL.VFS.Virtual_File is Class : constant Class_Type := Get_File_Class (Data); Inst : constant Class_Instance := Nth_Arg (Data, N, Class); begin return Get_Data (Inst); end Nth_Arg; ----------------------- -- Register_Commands -- ----------------------- procedure Register_Commands (Repo : access Scripts_Repository_Record'Class) is begin Register_Property (Repo, "executable_path", Class => Get_File_Class (Repo), Getter => File_Command_Handler'Access); Register_Command (Repo, Constructor_Method, Minimum_Args => 1, Maximum_Args => 2, Class => Get_File_Class (Repo), Handler => File_Command_Handler'Access); Register_Command (Repo, "name", Minimum_Args => 0, Maximum_Args => 1, Class => Get_File_Class (Repo), Handler => File_Command_Handler'Access); Repo.Register_Property ("path", Class => Get_File_Class (Repo), Getter => File_Command_Handler'Access); Register_Command (Repo, "directory", Class => Get_File_Class (Repo), Handler => File_Command_Handler'Access); Register_Command (Repo, "unit", Class => Get_File_Class (Repo), Handler => File_Command_Handler'Access); Register_Command (Repo, "other_file", Class => Get_File_Class (Repo), Handler => File_Command_Handler'Access); Register_Command (Repo, "project", Minimum_Args => 0, Maximum_Args => 1, Class => Get_File_Class (Repo), Handler => File_Command_Handler'Access); end Register_Commands; -------------- -- Set_Data -- -------------- procedure Set_Data (Instance : Class_Instance; File : Virtual_File) is begin if not Is_Subclass (Instance, File_Class_Name) then raise Invalid_Data; end if; Set_Data (Instance, File_Class_Name, File_Properties_Record'(File => File)); end Set_Data; ----------------- -- Set_Nth_Arg -- ----------------- procedure Set_Nth_Arg (Data : in out Callback_Data'Class; N : Positive; File : GNATCOLL.VFS.Virtual_File) is Inst : constant Class_Instance := Create_File (Get_Script (Data), File); begin Set_Nth_Arg (Data, N, Inst); end Set_Nth_Arg; end GNATCOLL.Scripts.Files; gnatcoll-core-21.0.0/src/gnatcoll_support.c0000644000175000017500000001310013661715457020531 0ustar nicolasnicolas/*---------------------------------------------------------------------------- -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -----------------------------------------------------------------------------*/ #include #include #include #include #ifdef _WIN32 #include #endif #ifdef HAVE_SYSLOG #include void syslog_wrapper(int priority, const char* msg) { syslog(priority, "%s", msg); } #endif // From adaint.h extern int __gnat_is_directory (char *); #ifdef HAVE_MMAP #include int gnatcoll_has_mmap() { return 1; } void * gnatcoll_mmap (void *start, long length, int prot, int flags, int fd, long offset) { return mmap (start, (size_t)length, prot, flags, fd, (off_t)offset); } int gnatcoll_munmap (void *start, long length) { return munmap (start, (size_t)length); } #ifdef HAVE_MADVISE void gnatcoll_madvise(void* addr, size_t len, int advice) { int adv = (advice == 1 ? MADV_NORMAL : advice == 2 ? MADV_RANDOM : advice == 4 ? MADV_SEQUENTIAL : MADV_NORMAL); madvise(addr, len, adv); } #else /* not HAVE_MADVISE */ void gnatcoll_madvise(void* addr, size_t len, int advice) { } #endif /* HAVE_MADVISE */ #else /* No mmap support, so no madvise support either */ int gnatcoll_has_mmap () { return 0; } void gnatcoll_madvise(void* addr, size_t len, int advice) { } void *gnatcoll_mmap (void *start, long length, int prot, int flags, int fd, long offset) { return (void*)0; } int gnatcoll_munmap (void *start, long length) { return 0; } #endif int __gnatcoll_get_logical_drive_strings (char *buffer, int len) { #ifdef _WIN32 return GetLogicalDriveStringsA ((DWORD)len, (LPSTR)buffer); #else return 0; #endif } void __gnatcoll_set_readable (char *file, int set) { #ifdef _WIN32 /* ??? NOT CURRENTLY SUPPORTED. There is no support for setting a file as unreadable using the standard chmod routine on Windows. With this routine it is only possible to set a file as read-only. To set a file as unreadable it is required to use the more complex [Get|Set]FileSecurity Win32 API by setting the proper ACL. */ #elif defined (__VMS__) /* ??? NOT CURRENTLY SUPPORTED. */ #else struct stat statbuf; if (!stat (file, &statbuf)) { if (set) chmod (file, statbuf.st_mode | S_IREAD); else chmod (file, statbuf.st_mode & (~S_IREAD)); } #endif } /********************************************************** ** __gnatcoll_get_tmp_dir () ** Return the tmp directory. ** Return value must be freed by caller **********************************************************/ char* __gnatcoll_get_tmp_dir (void) { static char *result = NULL; /* test static result to see if result has already been found */ if (result != NULL) return strdup (result); #ifdef _WIN32 { DWORD dwRet; result = malloc ((MAX_PATH + 1) * sizeof (char)); dwRet = GetTempPath (MAX_PATH, result); if (dwRet > 0) { result[dwRet] = '\0'; if (__gnat_is_directory (result)) return strdup (result); } free (result); } #endif result = getenv ("TMPDIR"); if (result) if (__gnat_is_directory (result)) return strdup (result); result = getenv ("TMP"); if (result) if (__gnat_is_directory (result)) return strdup (result); /* On Windows systems, this is the documented way of retrieving the tmp dir. * However, the TMP env variable should also be defined */ result = getenv ("TEMP"); if (result) if (__gnat_is_directory (result)) return strdup (result); /* need to duplicate twice: one is for caching, the second one will be freed * by user */ result = strdup ("/tmp"); return strdup (result); } /************************************************************************ * Support for atomic operations ************************************************************************/ bool gnatcoll_sync_bool_compare_and_swap_access (void** ptr, void* oldval, void* newval) { return __sync_bool_compare_and_swap(ptr, oldval, newval); } gnatcoll-core-21.0.0/src/set_std_prefix.c0000644000175000017500000000346713661715457020173 0ustar nicolasnicolas/*---------------------------------------------------------------------------- -- G N A T C O L L -- -- -- -- Copyright (C) 2002-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -----------------------------------------------------------------------------*/ /* Dummy version of set_std_prefix (needed by osint.adb) */ void set_std_prefix (char *path, int len) { } gnatcoll-core-21.0.0/src/gnatcoll-storage_pools-alignment.ads0000644000175000017500000000652613661715457024132 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2005-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides a storage pool that allows you to select any -- possible alignment for your data. -- The alignment itself is chosen through discriminant of the pool -- itself. -- -- My_Pool : Unbounded_No_Reclaim_Align_Pool (Alignment => 64); -- type My_Data is ...; -- for My_Data'Storage_Pool use My_Pool; with System.Storage_Pools; with System.Storage_Elements; package GNATCOLL.Storage_Pools.Alignment is pragma Elaborate_Body; -- Needed to ensure that library routines can execute allocators type Unbounded_No_Reclaim_Align_Pool (Alignment : System.Storage_Elements.Storage_Count) is new System.Storage_Pools.Root_Storage_Pool with private; -- A storage pool that uses malloc() internally, but always returns -- addresses aligned on Alignment bytes. private type Unbounded_No_Reclaim_Align_Pool (Alignment : System.Storage_Elements.Storage_Count) is new System.Storage_Pools.Root_Storage_Pool with null record; overriding function Storage_Size (Pool : Unbounded_No_Reclaim_Align_Pool) return System.Storage_Elements.Storage_Count; overriding procedure Allocate (Pool : in out Unbounded_No_Reclaim_Align_Pool; Address : out System.Address; Storage_Size : System.Storage_Elements.Storage_Count; Alignment : System.Storage_Elements.Storage_Count); overriding procedure Deallocate (Pool : in out Unbounded_No_Reclaim_Align_Pool; Address : System.Address; Storage_Size : System.Storage_Elements.Storage_Count; Alignment : System.Storage_Elements.Storage_Count); end GNATCOLL.Storage_Pools.Alignment; gnatcoll-core-21.0.0/src/gnatcoll-io-native-codec__unix.adb0000644000175000017500000000471013661715457023416 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- Unix version: todo, would require interfacing with 'locale' with Ada.Characters.Handling; use Ada.Characters.Handling; separate (GNATCOLL.IO.Native) package body Codec is ------------- -- To_UTF8 -- ------------- function To_UTF8 (Path : Wide_String) return String is begin return To_String (Path); end To_UTF8; function To_UTF8 (Path : FS_String) return String is begin return String (Path); end To_UTF8; --------------- -- From_UTF8 -- --------------- function From_UTF8 (Path : String) return FS_String is begin return FS_String (Path); end From_UTF8; function From_UTF8 (Path : String) return Wide_String is begin return To_Wide_String (Path); end From_UTF8; end Codec; gnatcoll-core-21.0.0/src/gnatcoll-vfs_utils.ads0000644000175000017500000001201113661715457021276 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNAT.Directory_Operations; use GNAT.Directory_Operations; with GNAT.OS_Lib; use GNAT.OS_Lib; with GNATCOLL.VFS; use GNATCOLL.VFS; private with GNATCOLL.Path; package GNATCOLL.VFS_Utils is -------------- -- Wrappers -- -------------- Local_Host_Is_Case_Sensitive : constant Boolean; -- These subprograms wrap around their equivalents in System.OS_Lib, and -- use Filesystem_String for better type safety. function Normalize_Pathname (Name : Filesystem_String; Directory : Filesystem_String := ""; Resolve_Links : Boolean := True; Case_Sensitive : Boolean := True) return Filesystem_String; function Is_Absolute_Path (Name : Filesystem_String) return Boolean; function Is_Regular_File (Name : Filesystem_String) return Boolean; function Is_Directory (Name : Filesystem_String) return Boolean; procedure Copy_File (Name : Filesystem_String; Pathname : Filesystem_String; Success : out Boolean; Mode : Copy_Mode := Copy; Preserve : Attribute := Time_Stamps); procedure Set_Writable (Name : Filesystem_String); procedure Set_Non_Writable (Name : Filesystem_String); procedure Set_Read_Only (Name : Filesystem_String) renames Set_Non_Writable; procedure Create_Temp_File (FD : out File_Descriptor; Name : out Filesystem_String_Access); function Locate_Exec_On_Path (Exec_Name : Filesystem_String) return Filesystem_String_Access; function Locate_Regular_File (File_Name : Filesystem_String; Path : Filesystem_String) return Filesystem_String_Access; -- These subprograms wrap around their equivalents in -- GNAT.Directory_Operations, and use Filesystem_String for better type -- safety. function File_Extension (Path : Filesystem_String) return Filesystem_String; function Get_Current_Dir return Filesystem_String; function Dir_Name (Path : Filesystem_String) return Filesystem_String; function Base_Name (Path : Filesystem_String; Suffix : Filesystem_String := "") return Filesystem_String; procedure Change_Dir (Dir_Name : Filesystem_String); function Format_Pathname (Path : Filesystem_String; Style : Path_Style := System_Default) return Filesystem_String; function Name_As_Directory (Name : Filesystem_String) return Filesystem_String; procedure Open (Dir : out Dir_Type; Dir_Name : Filesystem_String); -- These subprograms wrap around their equivalents in Ada.Directories, and -- use Filesystem_String for better type safety. function Compose (Containing_Directory : Filesystem_String := ""; Name : Filesystem_String; Extension : Filesystem_String := "") return Filesystem_String; ------------------------------------ -- Remote hosts handling of Files -- ------------------------------------ function Is_Case_Sensitive (Host : String) return Boolean; -- Tell if host's filesystem is case sensitive function File_Equal (F1, F2 : Filesystem_String; Host : String) return Boolean; private Local_Host_Is_Case_Sensitive : constant Boolean := GNATCOLL.Path.Is_Case_Sensitive (GNATCOLL.Path.Local_FS); end GNATCOLL.VFS_Utils; gnatcoll-core-21.0.0/src/gnatcoll-vfs_types.ads0000644000175000017500000000404213661715457021307 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Unchecked_Deallocation; package GNATCOLL.VFS_Types is type FS_String is new String; type FS_String_Access is access all FS_String; procedure Free is new Ada.Unchecked_Deallocation (FS_String, FS_String_Access); type FS_Type is (FS_Unknown, FS_Unix, FS_Unix_Case_Insensitive, FS_Windows); end GNATCOLL.VFS_Types; gnatcoll-core-21.0.0/src/gnatcoll-projects-normalize.ads0000644000175000017500000003427013661715457023122 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2002-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This internal unit provides support for editing projects. -- Project files can be written freely by the user (through any standard -- editor). However, although we are able to import them whatever form they -- have, these can't be easily manipulated, and a different form needs to be -- used, called normalized project files. -- -- Projects are normalized only the first time they are actually modified -- (i.e. if they are open in the project browser but never modified, we don't -- need to modify what the user did, since GPR.Proc.Process can of course work -- with any form of projects). -- -- However, the normalized projects are needed, so that we know exactly where -- to add new statements depending on the current scenario. -- -- Normalized projects have the following invariant: -- There is only one case statement per project or package. -- This is in fact a nested case statement, where each environment variable is -- referenced. -- -- They also have the following invariant: -- A project has exactly the same behavior in its normalized form as in -- its original form. -- Of course, this is only true until the next modification to any of the two -- forms. -- -- Thus, the projects have the following format: -- -- Project_Header -- [Variable_Declarations] -- [Common_Section] -- [Nested_Case] -- [Package_Declaration -- [Common_Section] -- [Nested_Case] -- ]* -- -- Where: -- Project_Header is the standard header, including importing other -- projects, declaring the name of the current project, ... -- -- Variable_Declarations is the list of scenario variables, including their -- types. There can be no variable declaration outside of this section, -- including in packages. -- ??? Not two variables can reference the same external variables. -- -- Common_Section is the list of statements that need to be executed in all -- scenarios (like common source directories, common switches when inside a -- package, ...). This section can not include any case statement. -- -- Nested_Case is one big case statement, including other nested cases. Its -- format is similar to: -- -- case Var1 is -- when Value1 => -- case Var2 is -- when Value1_1 => stmt1; -- when Value1_2 => stmt2; -- end case; -- when Value2 => -- case Var2 is -- when Value2_1 => stmt3; -- when Value2_2 => stmt4; -- end case; -- end case; -- -- The "when others" section is not allowed in the nested cases, and are -- replaced by the appropriate list of "when" statements. private package GNATCOLL.Projects.Normalize is Normalize_Error : exception; -- Raised when a project could not be normalized (necessary step before any -- modification). The project cannot be edited by GPS. -- Any subprogram in this package might raise this exception. In that case, -- the exception message is set to the text of the error. procedure Normalize (Tree : Project_Tree_Data_Access; Project : Project_Type); -- Normalize Project. -- The exception Normalize_Error is raised if Project uses some features -- that cannot currently be normalized. -- If Recurse is true, then imported projects area also normalized. function Get_String (Id : GPR.Name_Id) return String; function Get_String (Str : String) return GPR.Name_Id; function Clone_Node (Tree : GPR.Tree.Project_Node_Tree_Ref; Node : GPR.Project_Node_Id; Deep_Clone : Boolean := False) return GPR.Project_Node_Id; -- Return a copy of Node. If Deep_Clone is true, then all the children of -- node are also copied. -- If Deep_Clone is false, then the two nodes will share part of their -- structure. -- -- Note: nodes like variable or type declarations, packages,... are not -- chained up when they are cloned, you need to recreate the proper lists -- afterwards. See Post_Process_After_Clone below -- -- A special case also occurs for a N_Typed_Variable_Declaration, since the -- type that is referenced is a pointer to the same node as the type for -- Node. No deep copy is done for this type. This needs to be fixed in a -- post-processing phase, as above. -- -- The same limitation exists for N_Variable_Reference and -- N_Attribute_Reference and the package they are referencing function Is_Virtual_Extending (Tree : GPR.Tree.Project_Node_Tree_Ref; Node : GPR.Project_Node_Id) return Boolean; -- Return True if Node is a virtual extending project created -- automatically by GNAT's project manager function Find_Type_Declaration (Tree : GPR.Tree.Project_Node_Tree_Ref; Project : GPR.Project_Node_Id; Name : GPR.Name_Id) return GPR.Project_Node_Id; -- Return the declaration of the type whose name is Name function Create_Typed_Variable (Tree : GPR.Tree.Project_Node_Tree_Ref; Prj_Or_Pkg : GPR.Project_Node_Id; Name : String; Typ : GPR.Project_Node_Id; Add_Before_First_Case_Or_Pkg : Boolean := False) return GPR.Project_Node_Id; -- Create a new variable of a specific type Typ. -- The declaration is appended at the end of the declarative items list in -- the project or the package, unless Add_Before_First_Case is True. In -- this case, it is put just before the first N_Case_Construction node is -- encountered (i.e the last position in the common section of a normalized -- project). procedure Add_In_Front (Tree : GPR.Tree.Project_Node_Tree_Ref; Parent : GPR.Project_Node_Id; Node : GPR.Project_Node_Id); -- Add Node at the beginning of the list for Parent. -- Node can also be a N_Declarative_Item (or a list of them). procedure Normalize_Cases (Tree : GPR.Tree.Project_Node_Tree_Ref; Project : Project_Type); -- Make sure that all possible values of a variable appear in a case -- statement, to avoid warnings from the project manager. -- This subprogram doesn't apply recursively to imported projects function Add_Imported_Project (Tree : Project_Tree_Data_Access; Project : Project_Type'Class; Imported_Project : Project_Type'Class; Errors : Error_Report := null; Use_Relative_Path : Boolean; Use_Base_Name : Boolean; Limited_With : Boolean := False) return Import_Project_Error; -- Internal version of Add_Imported_Project. -- You must have computed the list of importing projects for Project. --------------- -- Variables -- --------------- function Is_External_Variable (Var : GPR.Project_Node_Id; Tree : GPR.Tree.Project_Node_Tree_Ref) return Boolean; -- Return True if Var is a reference to an external variable function External_Reference_Of (Var : GPR.Project_Node_Id; Tree : GPR.Tree.Project_Node_Tree_Ref) return GPR.Name_Id; -- Return the name of the external reference used in the declaration of -- Var (Var := external ("REF")). procedure Set_Value_As_External (Tree : GPR.Tree.Project_Node_Tree_Ref; Var : GPR.Project_Node_Id; External_Name : String; Default : String := ""); -- Set the value of the variable as a reference to the environment variable -- External_Name. Var must be a single value, not a string. -- If Var is a typed variable, the default value is checked against the -- list of possible values (Invalid_Value raised if not). function Create_Variable_Reference (Tree : GPR.Tree.Project_Node_Tree_Ref; Var : GPR.Project_Node_Id) return GPR.Project_Node_Id; -- Create and return a reference to the variable Var. -- Var must be a variable declaration procedure Delete_Scenario_Variable (Tree : Project_Tree_Data_Access; Root_Project : Project_Type; External_Name : String; Keep_Choice : String; Delete_Direct_References : Boolean := True); -- Internal version of Delete_External_Variable function Create_Type (Tree : GPR.Tree.Project_Node_Tree_Ref; Prj_Or_Pkg : GPR.Project_Node_Id; Name : String) return GPR.Project_Node_Id; -- Create a new type. By default, there is no possible value, you -- must add some with Add_Possible_Value. -- The new declaration is added at the end of the declarative item list for -- Prj_Or_Pkg (but before any package declaration). procedure Add_Possible_Value (Tree : GPR.Tree.Project_Node_Tree_Ref; Typ : GPR.Project_Node_Id; Choice : String); -- Add a new choice in the list of possible values for the type Typ. -- If Choice is already available in Typ, then it is not added again. function Expression_As_String (Tree : GPR.Tree.Project_Node_Tree_Ref; Expression : GPR.Project_Node_Id) return GPR.Name_Id; -- Return the string contained in an expression. If the expression contains -- more than a string literal, No_Name is returned. -- This also accepts cases when Expression itself is a string_literal function Find_Scenario_Variable (Tree : GPR.Tree.Project_Node_Tree_Ref; Project : Project_Type; External_Name : String) return GPR.Project_Node_Id; -- Return the declaration of the scenario variable associated with -- the external variable External_Name. -- In normalized projects, there should be only such variable. type Environment_Variable_Callback is access procedure (Project, Parent, Node, Choice : GPR.Project_Node_Id); -- Callback for For_Each_Environment_Variable. -- The various possible combinations are: -- Node Parent Choice -- N_Variable_Reference N_Term in expression Empty_Node -- N_External_Value N_Term in expression Empty_Node -- N_Case_Item N_Declarative_Item of matching choice -- case construction N_Literal_String -- N_String_Type_Declaration Empty_Node Empty_Node -- N_Typed_Variable_Declaration N_Declarative_Item Empty_Node procedure For_Each_Environment_Variable (Tree : GPR.Tree.Project_Node_Tree_Ref; Root_Project : Project_Type; Ext_Variable_Name : GPR.Name_Id; Specific_Choice : GPR.Name_Id; Action : Environment_Variable_Callback); -- Iterate over all possible references to an external variable. This -- returns N_External_Value, N_Variable_Reference, -- N_Typed_Variable_Declaration and N_String_Type_Declaration (the last -- three are indirect references through a named variable. ------------- -- Editing -- ------------- procedure Set_Attribute (Tree : Project_Tree_Data_Access; Project : Project_Type; Attribute : Attribute_Pkg_String; Value : String; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""; At_Index : Natural := 0); procedure Set_Attribute (Tree : Project_Tree_Data_Access; Project : Project_Type; Attribute : Attribute_Pkg_List; Values : GNAT.Strings.String_List; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""; Prepend : Boolean := False); -- Edit the value of an attribute procedure Delete_Attribute (Tree : Project_Tree_Data_Access; Project : Project_Type; Attribute : String; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""); -- Delete an attribute from the project procedure Rename_And_Move (Tree : Project_Tree_Data_Access; Project : Project_Type; New_Name : String; New_Path : GNATCOLL.VFS.Virtual_File; Errors : Error_Report := null); -- Internal version of Rename_And_Move function Rename_Path (Tree : Project_Tree_Data_Access; Project : Project_Type; Old_Path : GNATCOLL.VFS.Virtual_File; New_Path : GNATCOLL.VFS.Virtual_File; Use_Relative_Paths : Boolean) return Boolean; -- Internal version of Rename_Path end GNATCOLL.Projects.Normalize; gnatcoll-core-21.0.0/src/gnatcoll-coders-streams.ads0000644000175000017500000001466013661715457022227 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides Ada stream interface to the coders implemented the -- parent package abstraction Coder_Interface. Example of using this package -- can be found in examples/base64_coder.adb. with Ada.Containers.Indefinite_Holders; package GNATCOLL.Coders.Streams is type Stream_Type is new Root_Stream_Type with private; type Stream_Access is access all Root_Stream_Type'Class; type End_Of_Input_Method is (Empty_Read, Partial_Read, Explicit); -- Method to determine end of Read_From stream (See Initialize parameters -- below). -- Empty_Read means that the end of input stream is determined by last -- read from Read_From stream giving an empty result. -- Partial_Read means that the end of input stream is determined by last -- read from Read_From stream giving Partial result (Last < Item'Last). -- Explicit means that the end of input stream is determined by explicitly -- calling End_Of_Input. Note that a call to End_Of_Input procedure -- indicates the end of input in any case, independent of Read_Ends_By -- parameter of Initialize routine. Default_Buffer_Size : constant := 4096; -- Default buffer size for Read and Write operations. overriding procedure Read (Stream : in out Stream_Type; Item : out Stream_Element_Array; Last : out Stream_Element_Offset); overriding procedure Write (Stream : in out Stream_Type; Item : Stream_Element_Array); procedure Initialize (Stream : in out Stream_Type; Read_Coder : access Coder_Interface'Class := null; Write_Coder : access Coder_Interface'Class := null; Read_From : access Root_Stream_Type'Class := null; Write_To : access Root_Stream_Type'Class := null; Read_Ends_By : End_Of_Input_Method := Empty_Read; Read_Buffer_Size : Stream_Element_Count := Default_Buffer_Size; Write_Buffer_Size : Stream_Element_Count := Default_Buffer_Size); -- Sets read and/or write streams and coders for them. -- If Read pair is defined then Read operation is available. -- If Write pair is defined then Write operation is available. procedure Flush (Stream : in out Stream_Type; Mode : Flush_Mode := Sync_Flush); -- Flushes the written data to the Write_To stream, -- All data placed to the Write_Coder is flushed to the Write_To stream. -- Should not be used unless necessary, as it may e.g. degrade the -- compression quality in case when coder is compressor. procedure Flush_Read (Stream : in out Stream_Type; Item : out Stream_Element_Array; Last : out Stream_Element_Offset; Mode : Flush_Mode := Sync_Flush); -- Flushes read data from the Read_Coder and returns it in Item and Last -- out parameters. function Read_Total_In (Stream : Stream_Type) return Stream_Element_Count with Inline; -- Returns the total number of bytes read from Read_From stream so far function Read_Total_Out (Stream : Stream_Type) return Stream_Element_Count with Inline; -- Returns the total number of bytes read so far function Write_Total_In (Stream : Stream_Type) return Stream_Element_Count with Inline; -- Returns the total number of bytes written so far function Write_Total_Out (Stream : Stream_Type) return Stream_Element_Count with Inline; -- Returns the total number of bytes written to the Write_To stream procedure End_Of_Input (Stream : in out Stream_Type); -- Declares that input data is completed. Read routine is not going to -- read more data from Read_From stream. function End_Of_Input (Stream : Stream_Type) return Boolean; -- Returns True if data from Read_From stream is finished. private package SEA_Holders is new Ada.Containers.Indefinite_Holders (Stream_Element_Array); type Stream_Type is new Root_Stream_Type with record Read_Coder : access Coder_Interface'Class; Write_Coder : access Coder_Interface'Class; Read_From : access Root_Stream_Type'Class; Write_To : access Root_Stream_Type'Class; Read_Ends : End_Of_Input_Method := Empty_Read; End_Of_Read : Boolean := False; Buffer : SEA_Holders.Holder; Rest_First : Stream_Element_Offset; Rest_Last : Stream_Element_Offset; -- Buffer for Read operation. -- We need to have this buffer in the record because all read data -- from Read_From stream may not be processed during the read operation. Buffer_Size : Stream_Element_Offset; -- Buffer size for write operation. -- We do not need to have this buffer in the record because all data -- can be processed in the write operation. end record; end GNATCOLL.Coders.Streams; gnatcoll-core-21.0.0/src/getRSS.c0000644000175000017500000000726313661715457016316 0ustar nicolasnicolas/* * Author: David Robert Nadeau * Site: http://NadeauSoftware.com/ * License: Creative Commons Attribution 3.0 Unported License * http://creativecommons.org/licenses/by/3.0/deed.en_US */ #if defined(_WIN32) #include #include #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) #include #include #if defined(__APPLE__) && defined(__MACH__) #include #elif (defined(_AIX) || defined(__TOS__AIX__)) #include #include #elif (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) #include #include #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) #include #endif #else #error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS." #endif /** * Returns the peak (maximum so far) resident set size (physical * memory use) measured in bytes, or zero if the value cannot be * determined on this OS. */ size_t gnatcoll_getPeakRSS( ) { #if defined(_WIN32) /* Windows -------------------------------------------------- */ PROCESS_MEMORY_COUNTERS info; GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) ); return (size_t)info.PeakWorkingSetSize; #elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) /* AIX and Solaris ------------------------------------------ */ struct psinfo psinfo; int fd = -1; if ( (fd = open( "/proc/self/psinfo", O_RDONLY )) == -1 ) return (size_t)0L; /* Can't open? */ if ( read( fd, &psinfo, sizeof(psinfo) ) != sizeof(psinfo) ) { close( fd ); return (size_t)0L; /* Can't read? */ } close( fd ); return (size_t)(psinfo.pr_rssize * 1024L); #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) /* BSD, Linux, and OSX -------------------------------------- */ struct rusage rusage; getrusage( RUSAGE_SELF, &rusage ); #if defined(__APPLE__) && defined(__MACH__) return (size_t)rusage.ru_maxrss; #else return (size_t)(rusage.ru_maxrss * 1024L); #endif #else /* Unknown OS ----------------------------------------------- */ return (size_t)0L; /* Unsupported. */ #endif } /** * Returns the current resident set size (physical memory use) measured * in bytes, or zero if the value cannot be determined on this OS. */ size_t gnatcoll_getCurrentRSS( ) { #if defined(_WIN32) /* Windows -------------------------------------------------- */ PROCESS_MEMORY_COUNTERS info; GetProcessMemoryInfo( GetCurrentProcess( ), &info, sizeof(info) ); return (size_t)info.WorkingSetSize; #elif defined(__APPLE__) && defined(__MACH__) /* OSX ------------------------------------------------------ */ struct mach_task_basic_info info; mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; if ( task_info( mach_task_self( ), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount ) != KERN_SUCCESS ) return (size_t)0L; /* Can't access? */ return (size_t)info.resident_size; #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) /* Linux ---------------------------------------------------- */ long rss = 0L; FILE* fp = NULL; if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL ) return (size_t)0L; /* Can't open? */ if ( fscanf( fp, "%*s%ld", &rss ) != 1 ) { fclose( fp ); return (size_t)0L; /* Can't read? */ } fclose( fp ); return (size_t)rss * (size_t)sysconf( _SC_PAGESIZE); #else /* AIX, BSD, Solaris, and Unknown OS ------------------------ */ return (size_t)0L; /* Unsupported. */ #endif } gnatcoll-core-21.0.0/src/gnatcoll-os.ads0000644000175000017500000000430413661715457017707 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L . O S -- -- -- -- Copyright (C) 2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package GNATCOLL.OS is pragma Pure; -- Supported OS types type OS_Type is (Windows, Unix, MacOS); -- The filename resolution policy of a given file system type Filename_Casing_Policy is ( Lower_Case, -- case insensitive file system, normalized lower case Upper_Case, -- case insensitive file system, normalized upper case Preserving, -- case insensitive file system, case is preserved Sensitive -- case sensitive file system ); end GNATCOLL.OS; gnatcoll-core-21.0.0/src/gnatcoll-mmap-system__unix.ads0000644000175000017500000001031113661715457022737 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2007-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with System; with GNAT.OS_Lib; -- OS peculiarities abstraction package for Unix systems. package GNATCOLL.Mmap.System is type System_File is record Fd : GNAT.OS_Lib.File_Descriptor; Mapped : Boolean; -- Whether mapping is requested by the user and available on the system Write : Boolean; -- Whether this file can be written to Length : File_Size; -- Length of the file. Used to know what can be mapped in the file end record; type System_Mapping is record Address : Standard.System.Address; Length : File_Size; end record; Invalid_System_File : constant System_File := (GNAT.OS_Lib.Invalid_FD, False, False, 0); Invalid_System_Mapping : constant System_Mapping := (Standard.System.Null_Address, 0); function Open_Read (Filename : String; Use_Mmap_If_Available : Boolean := True) return System_File; -- Open a file for reading and return the corresponding System_File. Raise -- a Ada.IO_Exceptions.Name_Error if unsuccessful. function Open_Write (Filename : String; Use_Mmap_If_Available : Boolean := True) return System_File; -- Likewise for writing to a file procedure Close (File : in out System_File); -- Close a system file function Read_From_Disk (File : System_File; Offset, Length : File_Size) return GNAT.Strings.String_Access; -- Read a fragment of a file. It is up to the caller to free the result -- when done with it. -- Doesn't use mmap. procedure Write_To_Disk (File : System_File; Offset, Length : File_Size; Buffer : GNAT.Strings.String_Access); -- Write some content to a fragment of a file procedure Create_Mapping (File : System_File; Offset, Length : in out File_Size; Mutable : Boolean; Mapping : out System_Mapping; Advice : Use_Advice := Use_Normal); -- Create a memory mapping for the given File, for the area starting at -- Offset and containing Length bytes. Store it to Mapping. -- Note that Offset and Length may be modified according to the system -- needs (for boundaries, for instance). The caller must cope with actually -- wider mapped areas. procedure Dispose_Mapping (Mapping : in out System_Mapping); -- Unmap a previously-created mapping function Get_Page_Size return File_Size; -- Return the number of bytes in a system page. end GNATCOLL.Mmap.System; gnatcoll-core-21.0.0/src/gnatcoll-arg_lists.ads0000644000175000017500000001620613661715457021261 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides a type useful to manipulate command lines with GNAT.OS_Lib; with GNAT.Strings; with Ada.Containers.Vectors; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; package GNATCOLL.Arg_Lists is type Command_Line_Mode is (Raw_String, Separate_Args); -- There are two ways to treat a command line in GNATCOLL. -- Raw_String: these command lines should never be parsed for arguments -- and processing should be minimal. -- Separate_Args: these command lines need argument handling. type Argument_Mode is (Expandable, One_Arg); -- This type controls the behavior of arguments with respect to expansion. -- Expandable means that this argument can be expanded into multiple -- arguments. -- One_Arg means that this argument will only remain one argument, -- even if it gets expanded to separate space-separated strings. type Arg_List is private; -- A command line. -- This contains one command (an executable, typically) and a list of -- arguments. Empty_Command_Line : constant Arg_List; function Get_Command (C : Arg_List) return String; -- Return the command contained in C function Create (Command : String) return Arg_List; -- Create a command line from command. -- This creates a command line which has Command as a command and -- no arguments. function Argument_List_To_String (List : GNAT.Strings.String_List; Protect_Quotes : Boolean := True) return String; -- Concatenate all the elements in List into a single string. -- Argument_String_To_List (Argument_List_To_String (X)) = X -- The returned string ends with a space. -- If Protect_Quotes is True, then all quotes (single and double) are -- preceded by a backslash. function Parse_String (Text : String; Mode : Command_Line_Mode) return Arg_List; -- Parse Text and return a Arg_List, assuming that Text contains both -- the command and the arguments function Parse_String (Command : String; Text : String) return Arg_List; -- Return a command line, assuming Command contains the command and -- Text contains the arguments procedure Append_Argument (C : in out Arg_List; Argument : String; Mode : Argument_Mode); -- Append Argument to the list of arguments in C function Args_Length (C : Arg_List) return Integer; -- Return the length of the arguments. The command is not included in this -- count. -- Return 0 if there is only a command and no arguments. -- Return -1 if the command is empty. function Nth_Arg (C : Arg_List; N : Natural) return String; -- Return the Nth argument. Nth_Arg (0) returns the command function Nth_Arg (C : Arg_List; N : Natural) return Unbounded_String; -- Return the Nth argument. Nth_Arg (0) returns the command procedure Set_Nth_Arg (C : in out Arg_List; N : Natural; Arg : String); -- Set the Nth arg. -- If there are not enough args, create them. ------------------ -- Substitution -- ------------------ type Substitution_Function is access function (Param : String; Mode : Command_Line_Mode) return Arg_List; -- This type is preserved for backwards compatibility: it used to be the -- type of the Callback formal below. procedure Substitute (CL : in out Arg_List; Char : Character; Callback : access function (Param : String; Mode : Command_Line_Mode) return Arg_List); -- Substitute all parameters that start with Char using the mechanisms -- specified in Callback. function To_List (C : Arg_List; Include_Command : Boolean) return GNAT.OS_Lib.Argument_List; -- Return as an Argument_List: -- - the whole command line if Include_Command is True -- - only the arguments if Include_Command is False -- Caller must free the result. --------------------------- -- Conversions to string -- --------------------------- function To_Display_String (C : Arg_List; Include_Command : Boolean := True; Max_Arg_Length : Positive := Positive'Last) return String; -- Return a string that represents C, for display purposes. -- For instance -- cmd /c make LIBRARY_TYPE=static -- If Include_Command is False, display only the arguments. -- Max_Arg_Length is the maximum length returned for each argument in C. function To_Debug_String (C : Arg_List) return String; -- Return a string that represents C, for display purposes. -- For instance: -- command: "cmd" -- arg: "/c" -- arg: "make LIBRARY_TYPE=static" function To_Script_String (C : Arg_List) return String; -- Return a string that represents C, ready to be sent to a script -- For instance: -- cmd /c make\ LIBRARY_TYPE=static private type Argument_Type is record Mode : Argument_Mode; Text : Unbounded_String; end record; package Arg_List_Vector is new Ada.Containers.Vectors (Natural, Argument_Type); type Arg_List is record Mode : Command_Line_Mode := Separate_Args; V : Arg_List_Vector.Vector; -- The element number 0 is the command, and the following elements are -- arguments. end record; Empty_Command_Line : constant Arg_List := (Mode => Separate_Args, V => Arg_List_Vector.Empty_Vector); end GNATCOLL.Arg_Lists; gnatcoll-core-21.0.0/src/gnatcoll-io.ads0000644000175000017500000002301413661715457017674 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Calendar; with Ada.Strings.Unbounded; with GNAT.OS_Lib; with GNAT.Strings; with GNATCOLL.Strings; use GNATCOLL.Strings; with GNATCOLL.VFS_Types; use GNATCOLL.VFS_Types; private package GNATCOLL.IO is type Item_Type is (Unknown, -- File is not determined File, -- Regular file Directory -- Directory ); -- Item_Type is used to cache the calls to Is_Regular_File or Is_Directory -- that can be pretty time consuming, and that are performed pretty often. type File_Record is abstract tagged record Ref_Count : Natural := 0; Full : FS_String_Access; -- The file's full path Normalized : FS_String_Access; -- The file's normalized form ('..' and '.' directories removed) Normalized_And_Resolved : FS_String_Access; -- The normalized form with resolved symlinks. -- This points to the same value as Normalized if these have the same -- value. Kind : Item_Type := Unknown; -- The kind of file represented by this object end record; type File_Access is access all File_Record'Class; type File_Array is array (Natural range <>) of File_Access; procedure Ref (File : File_Access); procedure Unref (File : in out File_Access); procedure Destroy (File : in out File_Record); function Dispatching_Create (Ref : not null access File_Record; Full_Path : FS_String) return File_Access is abstract; -- Create a new file using the same tagged type is Ref function To_UTF8 (Ref : not null access File_Record; Path : FS_String) return String is abstract; function From_UTF8 (Ref : not null access File_Record; Path : String) return FS_String is abstract; -- Translate a path to/from UTF8 encoded strings, according to the -- Filesystem's charset. function Is_Local (File : File_Record) return Boolean is abstract; -- Tell if IO denotes a local file or directory function Get_FS (File : not null access File_Record) return FS_Type is abstract; -- Return the kind of FS the file is on procedure Resolve_Symlinks (File : not null access File_Record) is abstract; -- Resolve all potential symlinks present in the IO path. -- Does nothing if this computation has already been done. ---------------------- -- Queries on files -- ---------------------- function Is_Regular_File (File : not null access File_Record) return Boolean is abstract; -- Return True if Local_Full_Name exists on the remote host function Size (File : not null access File_Record) return Long_Integer is abstract; -- Return the size of the file in bytes. function Is_Directory (File : not null access File_Record) return Boolean is abstract; -- Return True if File is in fact a directory function Is_Symbolic_Link (File : not null access File_Record) return Boolean is abstract; -- Whether the file is a symbolic link function File_Time_Stamp (File : not null access File_Record) return Ada.Calendar.Time is abstract; -- Return the timestamp for this file. -- If the Connection doesn't support this operation, or the file -- doesn't exists, it should return a date of No_Time, so as to force, when -- possible, a read operation from the caller. function Is_Writable (File : not null access File_Record) return Boolean is abstract; -- Return True if File is writable procedure Set_Writable (File : not null access File_Record; State : Boolean) is abstract; -- If Writable is True, make the file writable, otherwise make the file -- unwritable. function Is_Readable (File : not null access File_Record) return Boolean is abstract; -- Return True if File is readable procedure Set_Readable (File : not null access File_Record; State : Boolean) is abstract; -- If Readable is True, make the file readable, otherwise make the file -- unreadable. ---------------------- -- File operations -- ---------------------- procedure Rename (From : not null access File_Record; Dest : not null access File_Record; Success : out Boolean) is abstract; -- Rename From_Local_Name on the host to To_Local_Name on the same host. -- Return False if the renaming could not be performed. procedure Copy (From : not null access File_Record; Dest : FS_String; Success : out Boolean) is abstract; -- Copy a file into another one. -- To_Local_Name can be the name of the directory in which to copy the -- file, or the name of a file to be created. procedure Delete (File : not null access File_Record; Success : out Boolean) is abstract; -- Sends host a delete command for file function Read_Whole_File (File : not null access File_Record) return GNAT.Strings.String_Access is abstract; function Read_Whole_File (File : not null access File_Record) return GNATCOLL.Strings.XString is abstract; -- Return the contents of an entire file. -- If the file cannot be found, return null. -- The caller is responsible for freeing the returned memory. -- No special encoding/decoding for charsets is done on the file. procedure Open_Write (File : not null access File_Record; Append : Boolean := False; FD : out GNAT.OS_Lib.File_Descriptor; Error : out Ada.Strings.Unbounded.Unbounded_String) is abstract; -- Opens a file for writing. Return a file descriptor used to actually -- write. -- /!\ Do not call close directly on FD, but use the method below instead. procedure Close (File : not null access File_Record; FD : GNAT.OS_Lib.File_Descriptor; Success : out Boolean) is abstract; -- Closes FD and actually flushes the content to File if needed procedure Copy_File_Permissions (From, To : not null access File_Record; Success : out Boolean) is abstract; -- Copy all permissions (read, write, exec) from one file to the other, -- so that To ends up with the same permissions. This does not change -- the owner of the file. -------------------------- -- Directory management -- -------------------------- function Change_Dir (Dir : not null access File_Record) return Boolean is abstract; -- Change the current directory. -- This operation might not make sense for some remote file systems if a -- new connection is opened for every operation, since the context would -- be lost. However, it does make sense when the connection is permanent. function Read_Dir (Dir : not null access File_Record; Dirs_Only : Boolean := False; Files_Only : Boolean := False) return GNAT.Strings.String_List is abstract; -- Read the specified directory and returns a list of filenames -- (base names). If Dirs_Only is set, then the files returned are directory -- only. Same for Files_Only, concerning regular files. -- This does *not* return the two special directories "." and ".." function Make_Dir (Dir : not null access File_Record; Recursive : Boolean) return Boolean is abstract; -- Create a new directory on remote named Local_Dir_Name. -- Return the creation status. procedure Remove_Dir (Dir : not null access File_Record; Recursive : Boolean; Success : out Boolean) is abstract; -- Delete a directory. Recursive allow to remove included files or -- subdirectories. procedure Copy_Dir (From : not null access File_Record; Dest : FS_String; Success : out Boolean) is abstract; -- From_Local_Name is the name of a directory. All its files are copied -- into the directory To_Local_Name. The target directory is created if -- needed. end GNATCOLL.IO; gnatcoll-core-21.0.0/src/gnatcoll-scripts.adb0000644000175000017500000013360013743647711020734 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Calendar; use Ada.Calendar; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Unchecked_Conversion; with Ada.Unchecked_Deallocation; with GNAT.OS_Lib; use GNAT.OS_Lib; with GNATCOLL.Scripts.Impl; use GNATCOLL.Scripts.Impl; with GNATCOLL.Traces; use GNATCOLL.Traces; with System.Address_Image; package body GNATCOLL.Scripts is Me : constant Trace_Handle := Create ("SCRIPTS"); use Classes_Hash; use CI_Pointers; Timeout_Threshold : constant Duration := 0.2; -- in seconds -- Timeout between two checks of the gtk+ event queue function To_Address is new Ada.Unchecked_Conversion (Class_Instance_Record_Access, System.Address); procedure Internal_Register_Command (Repo : access Scripts_Repository_Record'Class; Command : String; Minimum_Args : Natural := 0; Maximum_Args : Natural := 0; Params : Param_Array_Access := null; Handler : Module_Command_Function; Class : Class_Type := No_Class; Static_Method : Boolean := False; Language : String := ""); -- Internal version of Register_Command function Get_Data (Instance : access Class_Instance_Record'Class; Name : String) return User_Data_List; -- Return the user data with the given name, or null if there is none procedure Unset_Data (Instance : access Class_Instance_Record'Class; Name : String); -- Remove the user data with the given name ----------------------------------- -- Data stored in class_instance -- ----------------------------------- type User_Data_Type is (Strings, Integers, Booleans, Consoles, Floats); type Scalar_Properties_Record (Typ : User_Data_Type) is new Instance_Property_Record with record case Typ is when Strings => Str : GNAT.Strings.String_Access; when Integers => Int : Integer; when Floats => Flt : Float; when Booleans => Bool : Boolean; when Consoles => Console : Virtual_Console; end case; end record; type Scalar_Properties is access all Scalar_Properties_Record'Class; overriding procedure Destroy (Prop : in out Scalar_Properties_Record); -- See inherited documentation ----------------- -- Subprograms -- ----------------- procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Scripting_Language_Array, Scripting_Language_List); procedure Free_User_Data (Data : in out User_Data_List); -- Free the memory used by Data. Data is reset to null, and this doesn't -- free other user data in the list. procedure Free (Param : in out Param_Descr); procedure Free (Params : in out Param_Array_Access); -- Free memory ------------- -- Destroy -- ------------- procedure Destroy (Prop : in out Instance_Property_Record) is pragma Unreferenced (Prop); begin null; end Destroy; ---------- -- Free -- ---------- procedure Free (Param : in out Param_Descr) is begin Free (Param.Name); end Free; ---------- -- Free -- ---------- procedure Free (Params : in out Param_Array_Access) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Param_Array, Param_Array_Access); begin if Params /= null then for P in Params'Range loop Free (Params (P)); end loop; Unchecked_Free (Params); end if; end Free; ------------- -- Destroy -- ------------- procedure Destroy (Repo : in out Scripts_Repository) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Scripts_Repository_Record'Class, Scripts_Repository); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Command_Descr, Command_Descr_Access); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Property_Descr, Property_Descr_Access); C : Classes_Hash.Cursor; Class : Class_Type; D, D_Tmp : Command_Descr_Access; Prop, P_Tmp : Property_Descr_Access; begin Trace (Me, "Destroying scripts repository"); if Repo /= null then D := Repo.Commands; while D /= null loop D_Tmp := D.Next; Free (D.Params); Unchecked_Free (D); D := D_Tmp; end loop; Prop := Repo.Properties; while Prop /= null loop P_Tmp := Prop.Next; Unchecked_Free (Prop); Prop := P_Tmp; end loop; if Repo.Scripting_Languages /= null then for L in Repo.Scripting_Languages'Range loop Destroy (Repo.Scripting_Languages (L)); -- Do not free the language itself, though. Since scripts are -- full of controlled types, it might happen that some of them -- will be freed later on, and they might still have pointers -- to the script itself. -- Unchecked_Free (Repo.Scripting_Languages (L)); end loop; Unchecked_Free (Repo.Scripting_Languages); end if; C := First (Repo.Classes); while Has_Element (C) loop Class := Element (C); Free (Class.Qualified_Name); Next (C); end loop; Unchecked_Free (Repo); end if; end Destroy; ---------- -- Free -- ---------- procedure Free (List : in out Instance_List) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Instance_Array, Instance_Array_Access); begin -- Class_Instance are automatically Finalized by the compiler Unchecked_Free (List.List); end Free; --------- -- Get -- --------- function Get (List : Instance_List; Script : access Scripting_Language_Record'Class) return Class_Instance is begin if List.List /= null then for Idx in List.List'Range loop exit when List.List (Idx) = No_Class_Instance; if Get_Script (List.List (Idx)) = Scripting_Language (Script) then return List.List (Idx); end if; end loop; end if; return No_Class_Instance; end Get; --------- -- Set -- --------- procedure Set (List : in out Instance_List; Inst : Class_Instance) is Idx : Natural; begin if List.List = null then declare Tmp : constant Scripting_Language_Array := Get_Repository (Get_Script (Inst)).Scripting_Languages.all; begin List.List := new Instance_Array (Tmp'Range); List.List.all := (others => No_Class_Instance); end; end if; Idx := List.List'First; while Idx <= List.List'Last loop if List.List (Idx).Ref.Get = null or else Get_Script (List.List (Idx)) = Get_Script (Inst) then List.List (Idx) := Inst; return; end if; Idx := Idx + 1; end loop; end Set; ----------- -- First -- ----------- function First (Self : Instance_List) return Inst_Cursor is begin if Self.List = null or else Self.List (Self.List'First).Ref.Get = null then return (Index => Natural'Last); else return (Index => Self.List'First); end if; end First; ---------- -- Next -- ---------- procedure Next (Self : Instance_List; Pos : in out Inst_Cursor) is begin if Pos.Index >= Self.List'Last or else Self.List (Pos.Index + 1).Ref.Get = null then Pos := (Index => Natural'Last); else Pos := (Index => Pos.Index + 1); end if; end Next; ----------------- -- Has_Element -- ----------------- function Has_Element (Position : Inst_Cursor) return Boolean is begin return Position.Index /= Natural'Last; end Has_Element; ------------- -- Element -- ------------- function Element (Self : Instance_List; Pos : Inst_Cursor) return Class_Instance is begin return Self.List (Pos.Index); end Element; ---------- -- Free -- ---------- procedure Free (List : in out Callback_Data_List) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Callback_Data_Array, Callback_Data_List); begin if List /= null then for L in List'Range loop if List (L) /= null then Free (List (L)); end if; end loop; Unchecked_Free (List); end if; end Free; --------- -- Get -- --------- function Get (Repo : access Scripts_Repository_Record'Class; List : Callback_Data_List; Script : access Scripting_Language_Record'Class) return Callback_Data_Access is Tmp : constant Scripting_Language_Array := Repo.Scripting_Languages.all; begin if List /= null then for T in Tmp'Range loop if Tmp (T) = Scripting_Language (Script) then return List (T); end if; end loop; end if; return null; end Get; --------- -- Set -- --------- procedure Set (Repo : access Scripts_Repository_Record'Class; List : in out Callback_Data_List; Script : access Scripting_Language_Record'Class; Data : Callback_Data_Access) is Tmp : constant Scripting_Language_Array := Repo.Scripting_Languages.all; begin if List = null then List := new Callback_Data_Array (Tmp'Range); end if; for T in Tmp'Range loop if Tmp (T) = Scripting_Language (Script) then if List (T) /= null and then List (T) /= Data then Free (List (T)); end if; List (T) := Data; exit; end if; end loop; end Set; ---------- -- Free -- ---------- procedure Free (Data : in out Callback_Data_Access) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Callback_Data'Class, Callback_Data_Access); begin if Data /= null then Free (Data.all); Unchecked_Free (Data); end if; end Free; --------------------------------- -- Register_Scripting_Language -- --------------------------------- procedure Register_Scripting_Language (Repo : access Scripts_Repository_Record'Class; Script : access Scripting_Language_Record'Class) is Tmp : constant Scripting_Language_Array := Repo.Scripting_Languages.all; begin Unchecked_Free (Repo.Scripting_Languages); Repo.Scripting_Languages := new Scripting_Language_Array'(Tmp & Scripting_Language (Script)); end Register_Scripting_Language; ------------------------------- -- Lookup_Scripting_Language -- ------------------------------- function Lookup_Scripting_Language (Repo : access Scripts_Repository_Record'Class; Name : String) return Scripting_Language is Tmp : constant Scripting_Language_List := Repo.Scripting_Languages; N : constant String := To_Lower (Name); begin for T in Tmp'Range loop if To_Lower (Get_Name (Tmp (T))) = N then return Tmp (T); end if; end loop; return null; end Lookup_Scripting_Language; ----------------------------- -- Get_Scripting_Languages -- ----------------------------- function Get_Scripting_Languages (Repo : access Scripts_Repository_Record'Class) return Scripting_Language_Array is begin return Repo.Scripting_Languages.all; end Get_Scripting_Languages; -------------------- -- Block_Commands -- -------------------- procedure Block_Commands (Repo : access Scripts_Repository_Record'Class; Block : Boolean) is Tmp : constant Scripting_Language_List := Repo.Scripting_Languages; begin for T in Tmp'Range loop Block_Commands (Tmp (T), Block); end loop; end Block_Commands; ----------- -- Param -- ----------- function Param (Name : String; Optional : Boolean := False) return Param_Descr is begin return Param_Descr' (Name => new String'(Name), Optional => Optional); end Param; ---------------------- -- Register_Command -- ---------------------- procedure Register_Command (Repo : access Scripts_Repository_Record'Class; Command : String; Params : Param_Array; Handler : Module_Command_Function; Class : Class_Type := No_Class; Static_Method : Boolean := False; Language : String := "") is Min : Natural := Params'Length; begin for P in Params'Range loop if Params (P).Optional then Min := Min - 1; end if; end loop; Internal_Register_Command (Repo, Command => Command, Minimum_Args => Min, Maximum_Args => Params'Length, Params => new Param_Array'(Params), Handler => Handler, Class => Class, Static_Method => Static_Method, Language => Language); end Register_Command; ------------------------------- -- Internal_Register_Command -- ------------------------------- procedure Internal_Register_Command (Repo : access Scripts_Repository_Record'Class; Command : String; Minimum_Args : Natural := 0; Maximum_Args : Natural := 0; Params : Param_Array_Access := null; Handler : Module_Command_Function; Class : Class_Type := No_Class; Static_Method : Boolean := False; Language : String := "") is Tmp : constant Scripting_Language_List := Repo.Scripting_Languages; Cmd : Command_Descr_Access; begin if Command = Constructor_Method and then Class = No_Class then raise Program_Error with "Constructors can only be specified for classes"; end if; if Static_Method and then Class = No_Class then raise Program_Error with "Static method can only be created for classes"; end if; Cmd := new Command_Descr' (Length => Command'Length, Command => Command, Handler => Handler, Class => Class, Params => Params, Static_Method => Static_Method, Minimum_Args => Minimum_Args, Maximum_Args => Maximum_Args, Next => null); for T in Tmp'Range loop if Language = "" or else Get_Name (Tmp (T)) = Language then Register_Command (Tmp (T), Cmd); end if; end loop; -- Only add it to the list afterward, so that Register_Command is not -- tempted to look at Next Cmd.Next := Repo.Commands; Repo.Commands := Cmd; end Internal_Register_Command; ---------------------- -- Register_Command -- ---------------------- procedure Register_Command (Repo : access Scripts_Repository_Record'Class; Command : String; Minimum_Args : Natural := 0; Maximum_Args : Natural := 0; Handler : Module_Command_Function; Class : Class_Type := No_Class; Static_Method : Boolean := False; Language : String := "") is begin Internal_Register_Command (Repo, Command => Command, Minimum_Args => Minimum_Args, Maximum_Args => Maximum_Args, Params => null, Handler => Handler, Class => Class, Static_Method => Static_Method, Language => Language); end Register_Command; ---------------------- -- Override_Command -- ---------------------- procedure Override_Command (Repo : access Scripts_Repository_Record'Class; Command : String; Handler : Module_Command_Function; Class : Class_Type := No_Class) is Cmd : Command_Descr_Access := Repo.Commands; begin while Cmd /= null loop if Cmd.Command = Command and then Cmd.Class = Class then Cmd.Handler := Handler; return; end if; Cmd := Cmd.Next; end loop; raise Program_Error with "Command " & Command & " not found"; end Override_Command; ----------------------- -- Register_Property -- ----------------------- procedure Register_Property (Repo : access Scripts_Repository_Record'Class; Name : String; Class : Class_Type; Setter : Module_Command_Function := null; Getter : Module_Command_Function := null) is Tmp : constant Scripting_Language_List := Repo.Scripting_Languages; Prop : Property_Descr_Access; begin if Setter = null and then Getter = null then raise Program_Error with "A property must have at least a getter or a setter"; end if; Prop := new Property_Descr' (Length => Name'Length, Name => Name, Class => Class, Getter => Getter, Setter => Setter, Next => null); for T in Tmp'Range loop Register_Property (Tmp (T), Prop); end loop; -- Only add it to the list afterward, so that Register_Command is not -- tempted to look at Next Prop.Next := Repo.Properties; Repo.Properties := Prop; end Register_Property; --------------- -- New_Class -- --------------- function New_Class (Repo : access Scripts_Repository_Record'Class; Name : String; Base : Class_Type := No_Class; Module : Module_Type := Default_Module) return Class_Type is Tmp : constant Scripting_Language_List := Repo.Scripting_Languages; Class : Class_Type; begin if Tmp = null then return No_Class; else Class := Lookup_Class (Repo, Name, Module); if not Class.Exists then Class.Exists := True; Include (Repo.Classes, Class.Qualified_Name.all, Class); for T in Tmp'Range loop Register_Class (Tmp (T), Name, Base, Module); end loop; end if; return Class; end if; end New_Class; ------------------ -- Lookup_Class -- ------------------ function Lookup_Class (Repo : access Scripts_Repository_Record; Name : String; Module : Module_Type := Default_Module) return Class_Type is C : Classes_Hash.Cursor; Class : Class_Type; function Qualified_Name return String; function Qualified_Name return String is begin if Module = Default_Module then return Name; else return To_String (Module.Name) & '.' & Name; end if; end Qualified_Name; N : constant String := Qualified_Name; begin C := Find (Repo.Classes, N); if Has_Element (C) then return Element (C); else Class := Class_Type' (Qualified_Name => new String'(N), Exists => False); Include (Repo.Classes, N, Class); return Class; end if; end Lookup_Class; ------------------- -- Lookup_Module -- ------------------- function Lookup_Module (Repo : access Scripts_Repository_Record; Qualified_Name : String) return Module_Type is pragma Unreferenced (Repo); begin return Module_Type'(Name => To_Unbounded_String (Qualified_Name)); end Lookup_Module; -------------- -- Get_Name -- -------------- function Get_Name (Class : Class_Type) return String is begin if Class.Qualified_Name = null then return ""; elsif Class.Qualified_Name'Length > 2 and then Class.Qualified_Name (Class.Qualified_Name'First .. Class.Qualified_Name'First + 1) = "@." then return Class.Qualified_Name (Class.Qualified_Name'First + 2 .. Class.Qualified_Name'Last); else return Class.Qualified_Name.all; end if; end Get_Name; ------------------------------- -- Register_Standard_Classes -- ------------------------------- procedure Register_Standard_Classes (Repo : access Scripts_Repository_Record'Class; Console_Class_Name : String; Logger_Class_Name : String := "") is begin Repo.Console_Class := New_Class (Repo, Console_Class_Name); Register_Console_Class (Repo, Repo.Console_Class); if Logger_Class_Name /= "" then Repo.Logger_Class := New_Class (Repo, Logger_Class_Name); Register_Logger_Class (Repo, Repo.Logger_Class); end if; end Register_Standard_Classes; ------------------------------- -- Execute_Command_With_Args -- ------------------------------- function Execute_Command_With_Args (Script : access Scripting_Language_Record; CL : Arg_List) return String is pragma Unreferenced (Script, CL); begin raise Program_Error; return ""; end Execute_Command_With_Args; --------------------- -- Execute_Command -- --------------------- function Execute_Command (Script : access Scripting_Language_Record; CL : Arg_List; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : access Boolean) return String is begin Execute_Command (Scripting_Language (Script), CL, Console, Hide_Output, Show_Command, Errors.all); return ""; end Execute_Command; --------------------- -- Execute_Command -- --------------------- procedure Execute_Command (Script : access Scripting_Language_Record; Command : String; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : out Boolean) is begin Execute_Command (Scripting_Language (Script), Parse_String (Command, Command_Line_Treatment (Scripting_Language (Script))), Console, Hide_Output, Show_Command, Errors); end Execute_Command; --------------------- -- Execute_Command -- --------------------- function Execute_Command (Script : access Scripting_Language_Record; Command : String; Console : Virtual_Console := null; Hide_Output : Boolean := False; Errors : access Boolean) return Boolean is begin return Execute_Command (Scripting_Language (Script), Parse_String (Command, Command_Line_Treatment (Scripting_Language (Script))), Console, Hide_Output, Errors); end Execute_Command; --------------------- -- Execute_Command -- --------------------- function Execute_Command (Script : Scripting_Language; Command : String; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : access Boolean) return String is begin return Execute_Command (Script, Parse_String (Command, Command_Line_Treatment (Script)), Console, Hide_Output, Show_Command, Errors); end Execute_Command; --------------- -- Interrupt -- --------------- function Interrupt (Script : access Scripting_Language_Record) return Boolean is pragma Unreferenced (Script); begin return False; end Interrupt; -------------- -- Complete -- -------------- procedure Complete (Script : access Scripting_Language_Record; Input : String; Completions : out String_Lists.List) is pragma Unreferenced (Script, Input); begin Completions := String_Lists.Empty_List; end Complete; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Callback_Data; N : Positive; Default : Subprogram_Type) return Subprogram_Type is begin return Subprogram_Type'(Nth_Arg (Callback_Data'Class (Data), N)); exception when No_Such_Parameter => return Default; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Callback_Data; N : Positive; Default : String) return String is begin return Nth_Arg (Callback_Data'Class (Data), N); exception when No_Such_Parameter => return Default; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Callback_Data; N : Positive; Default : Filesystem_String) return Filesystem_String is begin return Nth_Arg (Callback_Data'Class (Data), N); exception when No_Such_Parameter => return Default; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Callback_Data; N : Positive; Default : Integer) return Integer is begin return Nth_Arg (Callback_Data'Class (Data), N); exception when No_Such_Parameter => return Default; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Callback_Data; N : Positive; Default : Float) return Float is begin return Nth_Arg (Callback_Data'Class (Data), N); exception when No_Such_Parameter => return Default; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Callback_Data; N : Positive; Default : Boolean) return Boolean is begin return Nth_Arg (Callback_Data'Class (Data), N); exception when No_Such_Parameter => return Default; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Callback_Data; N : Positive; Class : Class_Type := Any_Class; Default : Class_Instance; Allow_Null : Boolean := False) return Class_Instance is begin return Nth_Arg (Callback_Data'Class (Data), N, Class, Allow_Null); exception when No_Such_Parameter => return Default; end Nth_Arg; -------------------- -- Get_Repository -- -------------------- function Get_Repository (Data : Callback_Data) return Scripts_Repository is begin return Get_Repository (Get_Script (Callback_Data'Class (Data))); end Get_Repository; ---------- -- Free -- ---------- procedure Free (Subprogram : in out Subprogram_Type) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Subprogram_Record'Class, Subprogram_Type); begin if Subprogram /= null then Free (Subprogram.all); Unchecked_Free (Subprogram); end if; end Free; ------------- -- Execute -- ------------- function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return Boolean is Err : aliased Boolean; begin return Execute (Subprogram, Args, Err'Access); end Execute; function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return String is Err : aliased Boolean; begin return Execute (Subprogram, Args, Err'Access); end Execute; function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return Class_Instance is Err : aliased Boolean; begin return Execute (Subprogram, Args, Err'Access); end Execute; function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return List_Instance'Class is Err : aliased Boolean; begin return Execute (Subprogram, Args, Err'Access); end Execute; function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return Any_Type is Err : aliased Boolean; begin return Execute (Subprogram, Args, Err'Access); end Execute; function Execute (Subprogram : access Subprogram_Record'Class; Args : Callback_Data'Class) return GNAT.Strings.String_List is Err : aliased Boolean; begin return Execute (Subprogram, Args, Err'Access); end Execute; -------------------- -- Free_User_Data -- -------------------- procedure Free_User_Data (Data : in out User_Data_List) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (User_Data, User_Data_List); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Instance_Property_Record'Class, Instance_Property); begin if Data /= null then if Data.Prop /= null then Destroy (Data.Prop.all); Unchecked_Free (Data.Prop); end if; Unchecked_Free (Data); end if; end Free_User_Data; ------------------------- -- Free_User_Data_List -- ------------------------- procedure Free_User_Data_List (Data : in out User_Data_List) is D : User_Data_List; begin while Data /= null loop D := Data; Data := Data.Next; Free_User_Data (D); end loop; end Free_User_Data_List; ------------- -- Destroy -- ------------- procedure Destroy (Prop : in out Scalar_Properties_Record) is begin case Prop.Typ is when Strings => Free (Prop.Str); when Integers | Consoles | Booleans | Floats => null; end case; end Destroy; ------------- -- Get_CIR -- ------------- function Get_CIR (Inst : Class_Instance) return Class_Instance_Record_Access is begin return Class_Instance_Record_Access (Inst.Ref.Get); end Get_CIR; -------------------- -- Print_Refcount -- -------------------- function Print_Refcount (Instance : access Class_Instance_Record) return String is begin return "CI=(" & System.Address_Image (To_Address (Class_Instance_Record_Access (Instance))) & ')'; end Print_Refcount; ------------------- -- Get_User_Data -- ------------------- function Get_User_Data (Self : not null access Class_Instance_Record) return access User_Data_List is begin -- We could not make the operation abstract and private raise Program_Error with "Get_User_Data should be overridden"; return null; end Get_User_Data; -------------- -- Get_Data -- -------------- function Get_Data (Instance : access Class_Instance_Record'Class; Name : String) return User_Data_List is U : constant access User_Data_List := Instance.Get_User_Data; D : User_Data_List; begin if U /= null then D := U.all; while D /= null loop if D.Name = Name then return D; end if; D := D.Next; end loop; end if; return null; end Get_Data; -------------- -- Set_Data -- -------------- procedure Set_Data (Instance : Class_Instance; Name : String; Property : Instance_Property_Record'Class) is begin Set_Data (Instance.Ref.Get, Name, Property); end Set_Data; -------------- -- Set_Data -- -------------- procedure Set_Data (Instance : access Class_Instance_Record'Class; Name : String; Property : Instance_Property_Record'Class) is U : constant access User_Data_List := Instance.Get_User_Data; begin if U /= null then Unset_Data (Instance, Name); U.all := new User_Data' (Length => Name'Length, Name => Name, Next => U.all, Prop => new Instance_Property_Record'Class'(Property)); end if; end Set_Data; ---------------- -- Unset_Data -- ---------------- procedure Unset_Data (Instance : Class_Instance; Name : Class_Type) is begin Unset_Data (Instance.Ref.Get, Get_Name (Name)); end Unset_Data; ---------------- -- Unset_Data -- ---------------- procedure Unset_Data (Instance : Class_Instance; Name : String) is begin Unset_Data (Instance.Ref.Get, Name); end Unset_Data; ---------------- -- Unset_Data -- ---------------- procedure Unset_Data (Instance : access Class_Instance_Record'Class; Name : String) is U : constant access User_Data_List := Instance.Get_User_Data; D : User_Data_List; Previous : User_Data_List; begin if U /= null then D := U.all; while D /= null loop if D.Name = Name then if Previous = null then U.all := D.Next; else Previous.Next := D.Next; end if; Free_User_Data (D); return; end if; Previous := D; D := D.Next; end loop; end if; end Unset_Data; -------------- -- Set_Data -- -------------- procedure Set_Data (Instance : Class_Instance; Name : Class_Type; Value : String) is begin Set_Data (Instance, Get_Name (Name), Create_Property (Value)); end Set_Data; -------------- -- Set_Data -- -------------- procedure Set_Data (Instance : Class_Instance; Name : Class_Type; Value : Integer) is begin Set_Data (Instance, Get_Name (Name), Create_Property (Value)); end Set_Data; -------------- -- Set_Data -- -------------- procedure Set_Data (Instance : Class_Instance; Name : Class_Type; Value : Float) is begin Set_Data (Instance, Get_Name (Name), Create_Property (Value)); end Set_Data; -------------- -- Set_Data -- -------------- procedure Set_Data (Instance : Class_Instance; Name : Class_Type; Value : Boolean) is begin Set_Data (Instance, Get_Name (Name), Create_Property (Value)); end Set_Data; --------------------- -- Create_Property -- --------------------- function Create_Property (Val : Boolean) return Instance_Property_Record'Class is begin return Scalar_Properties_Record'(Typ => Booleans, Bool => Val); end Create_Property; --------------------- -- Create_Property -- --------------------- function Create_Property (Val : Integer) return Instance_Property_Record'Class is begin return Scalar_Properties_Record'(Typ => Integers, Int => Val); end Create_Property; --------------------- -- Create_Property -- --------------------- function Create_Property (Val : Float) return Instance_Property_Record'Class is begin return Scalar_Properties_Record'(Typ => Floats, Flt => Val); end Create_Property; --------------------- -- Create_Property -- --------------------- function Create_Property (Val : String) return Instance_Property_Record'Class is begin return Scalar_Properties_Record' (Typ => Strings, Str => new String'(Val)); end Create_Property; ---------------- -- As_Boolean -- ---------------- function As_Boolean (Prop : Instance_Property_Record'Class) return Boolean is begin return Scalar_Properties_Record (Prop).Bool; end As_Boolean; ---------------- -- As_Integer -- ---------------- function As_Integer (Prop : Instance_Property_Record'Class) return Integer is begin return Scalar_Properties_Record (Prop).Int; end As_Integer; -------------- -- As_Float -- -------------- function As_Float (Prop : Instance_Property_Record'Class) return Float is begin return Scalar_Properties_Record (Prop).Flt; end As_Float; --------------- -- As_String -- --------------- function As_String (Prop : Instance_Property_Record'Class) return String is begin return Scalar_Properties_Record (Prop).Str.all; end As_String; ---------------- -- Get_Method -- ---------------- function Get_Method (Instance : Class_Instance; Name : String) return Subprogram_Type is begin return Get_Method (Get_CIR (Instance), Name); end Get_Method; -------------- -- Get_Data -- -------------- function Get_Data (Instance : access Class_Instance_Record'Class; Name : String) return Instance_Property is D : constant User_Data_List := Get_Data (Instance, Name); begin if D = null then return null; else return D.Prop; end if; end Get_Data; -------------- -- Get_Data -- -------------- function Get_Data (Instance : Class_Instance; Name : String) return Instance_Property is U : User_Data_List := null; begin if Instance.Ref.Get /= null then U := Get_Data (Get_CIR (Instance), Name); end if; if U /= null then return U.Prop; else return null; end if; end Get_Data; -------------- -- Get_Data -- -------------- function Get_Data (Instance : Class_Instance; Name : Class_Type) return Integer is Prop : constant Instance_Property := Get_Data (Instance, Get_Name (Name)); begin return Scalar_Properties (Prop).Int; end Get_Data; -------------- -- Get_Data -- -------------- function Get_Data (Instance : Class_Instance; Name : Class_Type) return Float is Prop : constant Instance_Property := Get_Data (Instance, Get_Name (Name)); begin return Scalar_Properties (Prop).Flt; end Get_Data; -------------- -- Get_Data -- -------------- function Get_Data (Instance : Class_Instance; Name : Class_Type) return Boolean is Prop : constant Instance_Property := Get_Data (Instance, Get_Name (Name)); begin return Scalar_Properties (Prop).Bool; end Get_Data; -------------- -- Get_Data -- -------------- function Get_Data (Instance : Class_Instance; Name : Class_Type) return String is Prop : constant Instance_Property := Get_Data (Instance, Get_Name (Name)); begin return Scalar_Properties (Prop).Str.all; end Get_Data; ------------------ -- Set_Property -- ------------------ procedure Set_Property (Instance : Class_Instance; Name : String; Value : Integer) is CIR : constant Class_Instance_Record_Access := Get_CIR (Instance); begin if CIR /= null then Set_Property (CIR, Name, Value); end if; end Set_Property; ------------------ -- Set_Property -- ------------------ procedure Set_Property (Instance : Class_Instance; Name : String; Value : Float) is CIR : constant Class_Instance_Record_Access := Get_CIR (Instance); begin if CIR /= null then Set_Property (CIR, Name, Value); end if; end Set_Property; ------------------ -- Set_Property -- ------------------ procedure Set_Property (Instance : Class_Instance; Name : String; Value : Boolean) is CIR : constant Class_Instance_Record_Access := Get_CIR (Instance); begin if CIR /= null then Set_Property (CIR, Name, Value); end if; end Set_Property; ------------------ -- Set_Property -- ------------------ procedure Set_Property (Instance : Class_Instance; Name : String; Value : String) is CIR : constant Class_Instance_Record_Access := Get_CIR (Instance); begin if CIR /= null then Set_Property (CIR, Name, Value); end if; end Set_Property; ---------------- -- Get_Script -- ---------------- function Get_Script (Instance : Class_Instance) return Scripting_Language is begin return Scripting_Language (Instance.Ref.Get.Script); end Get_Script; ----------------- -- Is_Subclass -- ----------------- function Is_Subclass (Instance : Class_Instance; Base : Class_Type) return Boolean is begin return Is_Subclass (Get_CIR (Instance), Get_Name (Base)); end Is_Subclass; function Is_Subclass (Instance : Class_Instance; Base : String) return Boolean is begin return Is_Subclass (Get_CIR (Instance), Base); end Is_Subclass; ------------------------- -- Set_Default_Console -- ------------------------- procedure Set_Default_Console (Script : access Scripting_Language_Record; Console : Virtual_Console) is begin if Script.Console /= null then Set_As_Default_Console (Script.Console, null); end if; if Console /= null then Set_As_Default_Console (Console, Scripting_Language (Script)); end if; Script.Console := Console; Display_Prompt (Scripting_Language (Script)); end Set_Default_Console; ------------------------- -- Get_Default_Console -- ------------------------- function Get_Default_Console (Script : access Scripting_Language_Record) return Virtual_Console is begin return Script.Console; end Get_Default_Console; -------------- -- Set_Data -- -------------- procedure Set_Data (Instance : Class_Instance; Console : access Virtual_Console_Record'Class) is begin -- Note: even if Console is a widget, the call to Set_Data_Primitive -- below will automatically take care of proper reference counting, so -- that no additional work is needed Set_Data_Primitive (Instance, Console); Set_Data (Instance, "virtualconsole", Scalar_Properties_Record' (Typ => Consoles, Console => Virtual_Console (Console))); end Set_Data; -------------- -- Get_Data -- -------------- function Get_Data (Instance : Class_Instance) return Virtual_Console is D : constant Instance_Property := Get_Data (Instance, "virtualconsole"); begin if D = null or else D.all not in Scalar_Properties_Record'Class or else Scalar_Properties (D).Typ /= Consoles then return null; else return Scalar_Properties (D).Console; end if; end Get_Data; ----------------------- -- Get_Console_Class -- ----------------------- function Get_Console_Class (Repo : access Scripts_Repository_Record'Class) return Class_Type is begin return Repo.Console_Class; end Get_Console_Class; ---------- -- Read -- ---------- function Read (Console : access Virtual_Console_Record; Size : Integer; Whole_Line : Boolean; Prompt : String) return String is begin if Prompt /= "" then Insert_Prompt (Virtual_Console (Console), Prompt); end if; return Read (Virtual_Console (Console), Size, Whole_Line); end Read; ---------- -- Read -- ---------- function Read (Console : access Virtual_Console_Record; Size : Integer; Whole_Line : Boolean) return String is pragma Unreferenced (Console, Size, Whole_Line); begin return ""; end Read; ---------------------------- -- Process_Pending_Events -- ---------------------------- procedure Process_Pending_Events (Console : access Virtual_Console_Record'Class) is begin -- We mustn't do that if the commands are hidden, for some obscur -- reason found in GPS (python-gui.adb:1.25) if not Console.Hide_Output then if Clock - Console.Refresh_Timeout > Timeout_Threshold then Process_Pending_Events_Primitive (Console); Console.Refresh_Timeout := Clock; end if; end if; end Process_Pending_Events; ----------------- -- Set_Nth_Arg -- ----------------- procedure Set_Nth_Arg (Data : in out Callback_Data'Class; N : Positive; Value : Filesystem_String) is begin Set_Nth_Arg (Data, N, +(Value)); end Set_Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Callback_Data'Class; N : Positive) return Filesystem_String is begin return +Nth_Arg (Data, N); end Nth_Arg; ---------------------- -- Set_Return_Value -- ---------------------- procedure Set_Return_Value (Data : in out Callback_Data'Class; Value : Filesystem_String) is begin Set_Return_Value (Data, +Value); end Set_Return_Value; -------------- -- Load_All -- -------------- function Load_All (File : GNATCOLL.VFS.Virtual_File) return Boolean is pragma Unreferenced (File); begin return True; end Load_All; end GNATCOLL.Scripts; gnatcoll-core-21.0.0/src/gnatcoll-strings_impl.adb0000644000175000017500000022030613661715457021761 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Strings; use Ada.Strings; with Ada.Unchecked_Conversion; with GNATCOLL.Atomic; use GNATCOLL.Atomic; with GNATCOLL.Refcount; with System.Memory; use System.Memory; pragma Warnings (Off, ".*is an internal GNAT unit"); with System.String_Hash; pragma Warnings (On, ".*is an internal GNAT unit"); package body GNATCOLL.Strings_Impl is Page_Size : constant := 4096; -- Memory page size -------------------- -- Default_Growth -- -------------------- function Default_Growth (Current, Min_Size : String_Size) return String_Size is -- Compute minimum new size. -- 1.5 is often considered the best strategy, between efficiency -- and memory usage. New_Size : constant String_Size := String_Size'Max (Current * 3 / 2, Min_Size); begin if New_Size > Page_Size then -- Round up to the nearest page size, since this is what -- the system allocates anyway. This will always lead an even -- number. return (New_Size / Page_Size + 1) * Page_Size; else -- Must be an even number return New_Size + (New_Size and 1); end if; end Default_Growth; ------------- -- Strings -- ------------- package body Strings is function Convert is new Ada.Unchecked_Conversion (System.Address, Char_Array); function Convert is new Ada.Unchecked_Conversion (System.Address, Big_String_Data_Access); function Convert is new Ada.Unchecked_Conversion (Big_String_Data_Access, System.Address); Unshareable : constant GNATCOLL.Atomic.Atomic_Counter := GNATCOLL.Atomic.Atomic_Counter'Last; -- This is used as the refcount for strings that must not be -- shared. In particular, this is the case when we have taken -- a variable reference to a string, to fix the following case: -- R := S (2); -- a variable indexing -- S2 := S; -- share the buffer ??? WRONG -- R := 'A'; -- also alters S2 -- -- See http://www.gotw.ca/gotw/044.htm for a C++ discussion on why -- we need to make a string unshareable. Bytes_Per_Char : constant size_t := Char_Type'Size / Character'Size; -- Number of bytes for each character in the string. Extra_Header_Size : constant System.Memory.size_t := (if Copy_On_Write then System.Memory.size_t (GNATCOLL.Atomic.Atomic_Counter'Size / 8) else 0); -- Extra bytes needed for Big_String_Data in addition to the -- byte data stored in Unconstrained_String. procedure Store_Capacity (Self : in out XString; Capacity : String_Size) with Inline; function Get_Capacity (Self : XString) return String_Size is (2 * Self.Data.Big.Half_Capacity); -- Returns the current capacity of a large string procedure Store_Size (Self : in out XString; Size : Natural) with Inline; -- Store the size of Self. procedure Clone (Self : in out XString; Data : Big_String_Data_Access) with Pre => Self.Data.Small.Is_Big, Inline; -- Set the big string data, copying from Data. -- We copy the data from the parameter and not from Self.Data.Big.Data -- because the latter might already have been set to null at that -- point. -- New memory is allocated. procedure Make_Writable_Thread_Safe (Self : in out XString) with Inline; procedure Make_Writable_Thread_Unsafe (Self : in out XString) with Inline; Make_Writable : constant not null access procedure (Self : in out XString) := (if GNATCOLL.Refcount.Application_Uses_Tasks then Make_Writable_Thread_Safe'Access else Make_Writable_Thread_Unsafe'Access); -- Make sure we can modify Self (not a shared string) -- Two versions are provided: the Unsafe version is faster, but will -- fail when a string is read from a thread and written in another one, -- as in the following scenario: -- thread 1 | thread 2 -- S.Set ("some long long long str"); | -- Append (S, "some long long str"); | -- -- stops after testing refcount | -- | S2 := S; -- | -- buffer is now shared -- | Put_Line (S2.To_String); -- -- modifies shared buffer | -- | Put_Line (S2.To_String); -- | -- different output procedure Convert_To_Big_String (Self : in out XString; Size : String_Size) with Inline, Pre => not Self.Data.Small.Is_Big; -- Convert to a big string, by copying the small string data. -- We never convert back to a small string afterwards, to benefit from -- the memory we already allocated. -- This procedure does not copy the actual string, only allocates -- memory. -- Sets the size of the string -------------------- -- Store_Capacity -- -------------------- procedure Store_Capacity (Self : in out XString; Capacity : String_Size) is begin Self.Data.Big.Half_Capacity := Capacity / 2; end Store_Capacity; ---------------- -- Store_Size -- ---------------- procedure Store_Size (Self : in out XString; Size : Natural) is begin if Self.Data.Big.Is_Big then Self.Data.Big.Size := String_Size (Size); else Self.Data.Small.Size := SSize (Size); end if; end Store_Size; --------------------------------- -- Make_Writable_Thread_Unsafe -- --------------------------------- procedure Make_Writable_Thread_Unsafe (Self : in out XString) is begin if not Copy_On_Write or else not Self.Data.Small.Is_Big or else Self.Data.Big.Data.Refcount = Unshareable then null; -- nothing to do elsif Self.Data.Big.Data.Refcount = 1 then null; else Decrement (Self.Data.Big.Data.Refcount); Clone (Self, Self.Data.Big.Data); end if; end Make_Writable_Thread_Unsafe; ------------------------------- -- Make_Writable_Thread_Safe -- ------------------------------- procedure Make_Writable_Thread_Safe (Self : in out XString) is Tmp : Big_String_Data_Access; begin if not Copy_On_Write or else not Self.Data.Small.Is_Big or else Self.Data.Big.Data.Refcount = Unshareable then null; -- nothing to do else -- ??? We do not need an atomic sync_bool_compare_and_swap, -- since a string is not shared among threads (although the -- internal storage might be). Tmp := Self.Data.Big.Data; Self.Data.Big.Data := null; -- Now we know that Self.Data.Big.Data is null, and Tmp is -- set to the previous value. We still own a reference to -- that previous value, so it won't be freed by another -- thread. -- If another thread tries to do an assignment now, it will -- end up with a null buffer. But that is only possible if -- the other thread is accessing a shared string, which is -- not supported (a thread reading the string while we are -- modifying it). if Decrement (Tmp.Refcount) then -- We were the only user, so it is safe to keep the string Unsafe_Increment (Tmp.Refcount); Self.Data.Big.Data := Tmp; else -- Other threads were still sharing the data. We have to -- make a copy Clone (Self, Tmp); end if; end if; end Make_Writable_Thread_Safe; ----------- -- Clone -- ----------- procedure Clone (Self : in out XString; Data : Big_String_Data_Access) is Size : constant Integer := Integer (Self.Data.Big.Size); First : constant Natural := Natural (Self.Data.Big.First); Cap : constant String_Size := Growth_Strategy (0, Min_Size => Self.Data.Big.Size); Result : constant Big_String_Data_Access := Convert (System.Memory.Alloc (size_t (Cap) * Bytes_Per_Char + Extra_Header_Size)); begin if Copy_On_Write then Result.Refcount := 1; Result.Bytes2 (1 .. Size) := Data.Bytes2 (First .. First - 1 + Size); else Result.Bytes1 (1 .. Size) := Data.Bytes1 (First .. First - 1 + Size); end if; Self.Data.Big.First := 1; Store_Capacity (Self, Cap); Self.Data.Big.Data := Result; end Clone; ------------ -- Adjust -- ------------ overriding procedure Adjust (Self : in out XString) is begin if not Self.Data.Small.Is_Big then null; -- nothing to do elsif Copy_On_Write and then Self.Data.Big.Data.Refcount /= Unshareable then Increment (Self.Data.Big.Data.Refcount); else -- We do not need atomic operations here. We are still in -- the thread that did the assignment, and there is no -- shared data in this mode. Clone (Self, Self.Data.Big.Data); end if; end Adjust; -------------- -- Finalize -- -------------- overriding procedure Finalize (Self : in out XString) is Tmp : Big_String_Data_Access; begin -- nothing to do for small strings if Self.Data.Small.Is_Big then Tmp := Self.Data.Big.Data; if Tmp /= null then Self.Data.Big.Data := null; if not Copy_On_Write or else Tmp.Refcount = Unshareable or else Decrement (Tmp.Refcount) then System.Memory.Free (Convert (Tmp)); end if; end if; end if; end Finalize; ----------- -- Clear -- ----------- procedure Clear (Self : in out XString) is begin Finalize (Self); Self.Data.Small.Is_Big := False; Self.Data.Small.Size := 0; end Clear; --------------------------- -- Convert_To_Big_String -- --------------------------- procedure Convert_To_Big_String (Self : in out XString; Size : String_Size) is New_Size : constant String_Size := Growth_Strategy (0, Size); begin Store_Capacity (Self, New_Size); Self.Data.Big.Is_Big := True; Self.Data.Big.Data := Convert (System.Memory.Alloc (size_t (New_Size) * Bytes_Per_Char + Extra_Header_Size)); Self.Data.Big.Size := Size; Self.Data.Big.First := 1; if Copy_On_Write then Self.Data.Big.Data.Refcount := 1; end if; end Convert_To_Big_String; ------------- -- Reserve -- ------------- procedure Reserve (Self : in out XString; Capacity : String_Size) is Current_Cap : String_Size; First : Natural; New_Size : String_Size; Old_Size : Natural; begin if Self.Data.Small.Is_Big then -- We are about to modify the string if Copy_On_Write then Make_Writable (Self); end if; Current_Cap := Get_Capacity (Self); First := Self.Data.Big.First; -- Do we have enough space at the end already (i.e -- the capacity we really need extends from First, not -- from character 1). if Current_Cap >= String_Size (First) - 1 + Capacity then -- nothing to do, we have enough space null; else -- We'll have to make space. The simplest is first to move all -- characters back to First=1, which might free enough space at -- the end of the string. if First > 1 then Old_Size := Natural (Self.Data.Big.Size); if Copy_On_Write then Self.Data.Big.Data.Bytes2 (1 .. Old_Size) := Self.Data.Big.Data.Bytes2 (First .. First - 1 + Old_Size); else Self.Data.Big.Data.Bytes1 (1 .. Old_Size) := Self.Data.Big.Data.Bytes1 (First .. First - 1 + Old_Size); end if; Self.Data.Big.First := 1; end if; -- Do we have enough space now ? if Current_Cap < Capacity then New_Size := Growth_Strategy (Current_Cap, Capacity); Store_Capacity (Self, New_Size); Self.Data.Big.Data := Convert (System.Memory.Realloc (Convert (Self.Data.Big.Data), size_t (New_Size) * Bytes_Per_Char + Extra_Header_Size)); end if; end if; else -- If we'll need a large string if Capacity > Max_Small_Length then declare Current : constant Natural := Natural (Self.Data.Small.Size); Old : constant Char_String := Self.Data.Small.Data (1 .. Current); begin Convert_To_Big_String (Self, Capacity); pragma Assert (Self.Data.Big.First = 1); Self.Data.Big.Size := String_Size (Current); if Copy_On_Write then Self.Data.Big.Data.Bytes2 (1 .. Current) := Convert (Old'Address) (1 .. Current); else Self.Data.Big.Data.Bytes1 (1 .. Current) := Convert (Old'Address) (1 .. Current); end if; end; end if; end if; end Reserve; ------------ -- Shrink -- ------------ procedure Shrink (Self : in out XString) is New_Size : String_Size; begin if not Self.Data.Small.Is_Big then -- Nothing to do null; else -- ??? Should we try to revert to a small string Make_Writable (Self); New_Size := Growth_Strategy (0, Self.Data.Big.Size); Store_Capacity (Self, New_Size); Self.Data.Big.Data := Convert (System.Memory.Realloc (Convert (Self.Data.Big.Data), size_t (New_Size) * Bytes_Per_Char + Extra_Header_Size)); end if; end Shrink; --------- -- Set -- --------- procedure Set (Self : in out XString; Str : Char_String) is begin Store_Size (Self, 0); Self.Reserve (Capacity => Str'Length); if not Self.Data.Small.Is_Big then Self.Data.Small.Size := Str'Length; Self.Data.Small.Data (1 .. Str'Length) := Str; else Self.Data.Big.Size := String_Size (Str'Length); Self.Data.Big.First := 1; if Copy_On_Write then Self.Data.Big.Data.Bytes2 (1 .. Str'Length) := Convert (Str'Address) (1 .. Str'Length); else Self.Data.Big.Data.Bytes1 (1 .. Str'Length) := Convert (Str'Address) (1 .. Str'Length); end if; end if; end Set; ------------ -- Append -- ------------ procedure Append (Self : in out XString; Str : Char_String) is Current : constant Natural := Self.Length; New_Size : constant Natural := Current + Str'Length; F : Natural; begin if Str'Length = 0 then return; end if; -- Make sure we have enough space, possibly by moving -- characters back to position 1, or by converting to -- a big string, or resizing the current buffer. Self.Reserve (Capacity => String_Size (New_Size)); if not Self.Data.Small.Is_Big then Self.Data.Small.Data (Current + 1 .. New_Size) := Str; Self.Data.Small.Size := SSize (New_Size); else F := Natural (Self.Data.Big.First) + Current; Self.Data.Big.Size := String_Size (New_Size); if Copy_On_Write then Self.Data.Big.Data.Bytes2 (F .. F - 1 + Str'Length) := Convert (Str'Address) (1 .. Str'Length); else Self.Data.Big.Data.Bytes1 (F .. F - 1 + Str'Length) := Convert (Str'Address) (1 .. Str'Length); end if; end if; end Append; ------------ -- Append -- ------------ procedure Append (Self : in out XString; Char : Char_Type) is Current : constant Natural := Self.Length; F : Natural; begin Self.Reserve (Capacity => String_Size (Current + 1)); if not Self.Data.Small.Is_Big then Self.Data.Small.Data (Current + 1) := Char; Self.Data.Small.Size := SSize (Current + 1); else F := Natural (Self.Data.Big.First) + Current; Self.Data.Big.Size := String_Size (Current + 1); if Copy_On_Write then Self.Data.Big.Data.Bytes2 (F) := Char; else Self.Data.Big.Data.Bytes1 (F) := Char; end if; end if; end Append; ------------ -- Append -- ------------ procedure Append (Self : in out XString; Str : XString) is B : Char_Array; L : Natural; begin if Self.Length = 0 then -- Share the string instead of malloc+copy Self := Str; else Get_String (Str, B, L); if L /= 0 then Self.Append (Char_String (B (1 .. L))); end if; end if; end Append; --------- -- "*" -- --------- function "*" (Count : Natural; Right : Char_Type) return XString is Result : XString; begin Result.Reserve (Capacity => String_Size (Count)); for C in 1 .. Count loop Result.Append (Right); end loop; return Result; end "*"; --------- -- "*" -- --------- function "*" (Count : Natural; Right : Char_String) return XString is Result : XString; begin Result.Reserve (Capacity => String_Size (Count * Right'Length)); for C in 1 .. Count loop Result.Append (Right); end loop; return Result; end "*"; --------- -- "*" -- --------- function "*" (Count : Natural; Right : XString) return XString is Result : XString; begin Result.Reserve (Capacity => String_Size (Count * Right.Length)); for C in 1 .. Count loop Result.Append (Right); end loop; return Result; end "*"; ------------ -- Length -- ------------ function Length (Self : XString) return Natural is begin if not Self.Data.Small.Is_Big then return Natural (Self.Data.Small.Size); else return Natural (Self.Data.Big.Size); end if; end Length; ---------------- -- Get_String -- ---------------- procedure Get_String (Self : XString; S : out Char_Array; L : out Natural) is begin if not Self.Data.Small.Is_Big then L := Natural (Self.Data.Small.Size); S := Convert (Self.Data.Small.Data'Address); -- For a big string, we need to take into account First. Yet, -- everything should behave for the user as if the first character -- was always at index 1. elsif Copy_On_Write then L := Natural (Self.Data.Big.Size); S := Convert (Self.Data.Big.Data.Bytes2 (Natural (Self.Data.Big.First))'Address); else L := Natural (Self.Data.Big.Size); S := Convert (Self.Data.Big.Data.Bytes1 (Natural (Self.Data.Big.First))'Address); end if; end Get_String; ------------------- -- Access_String -- ------------------- procedure Access_String (Self : XString; Process : not null access procedure (S : Char_String)) is Copy : XString; To_Decref : Big_String_Data_Access; S : Char_Array; L : Natural; begin -- Self is passed by reference, so its refcount might still be 1. -- As a result, if Process would access the original variable and -- for instance Append to it, we would end up modifying the internal -- value of Self, and thus our S variable might end up referencing -- freed memory, same for Process. if not Self.Data.Small.Is_Big or else not Copy_On_Write then -- We must make a copy, since the internal data might change Copy := Self; Get_String (Copy, S, L); Process (Char_String (S (S'First .. L))); else if Self.Data.Big.Data.Refcount /= Unshareable then -- If anyone tries to modify the string, it will make a copy. -- But we might not have to make the copy at all. To_Decref := Self.Data.Big.Data; Increment (To_Decref.Refcount); end if; Get_String (Self, S, L); Process (Char_String (S (S'First .. L))); if To_Decref /= null then -- It is possible that we are now holding on to the last ref to -- the string, if Process has reset it to null for instance. if Decrement (To_Decref.Refcount) then System.Memory.Free (Convert (To_Decref)); end if; end if; end if; end Access_String; ---------------- -- To_XString -- ---------------- function To_XString (Str : Char_String) return XString is R : XString; begin R.Set (Str); return R; end To_XString; --------------- -- To_String -- --------------- function To_String (Self : XString) return Char_String is B : Char_Array; L : Natural; begin Get_String (Self, B, L); return Char_String (B (1 .. L)); end To_String; --------- -- "=" -- --------- function "=" (Left : XString; Right : Char_String) return Boolean is B : Char_Array; L : Natural; begin Get_String (Left, B, L); return Char_String (B (1 .. L)) = Right; end "="; --------- -- "=" -- --------- function "=" (Left, Right : XString) return Boolean is B1, B2 : Char_Array; L1, L2 : Natural; begin Get_String (Left, B1, L1); Get_String (Right, B2, L2); -- ??? Should we check the pointers and "First" return L1 = L2 and then B1 (1 .. L1) = B2 (1 .. L2); end "="; --------- -- "<" -- --------- function "<" (Left : XString; Right : Char_String) return Boolean is B : Char_Array; L : Natural; begin Get_String (Left, B, L); return Char_String (B (1 .. L)) < Right; end "<"; --------- -- "<" -- --------- function "<" (Left : Char_String; Right : XString) return Boolean is B : Char_Array; L : Natural; begin Get_String (Right, B, L); return Left < Char_String (B (1 .. L)); end "<"; --------- -- "<" -- --------- function "<" (Left, Right : XString) return Boolean is B, B2 : Char_Array; L, L2 : Natural; begin Get_String (Left, B, L); Get_String (Right, B2, L2); return B (1 .. L) < B2 (1 .. L2); end "<"; ---------- -- "<=" -- ---------- function "<=" (Left : XString; Right : Char_String) return Boolean is B : Char_Array; L : Natural; begin Get_String (Left, B, L); return Char_String (B (1 .. L)) <= Right; end "<="; ---------- -- "<=" -- ---------- function "<=" (Left : Char_String; Right : XString) return Boolean is B : Char_Array; L : Natural; begin Get_String (Right, B, L); return Left <= Char_String (B (1 .. L)); end "<="; ---------- -- "<=" -- ---------- function "<=" (Left, Right : XString) return Boolean is B, B2 : Char_Array; L, L2 : Natural; begin Get_String (Left, B, L); Get_String (Right, B2, L2); return B (1 .. L) <= B2 (1 .. L2); end "<="; ------------- -- Compare -- ------------- function Compare (Left : XString; Right : Char_String) return Compare_Result is S : Char_Array; L : Natural; C2 : Char_Type; begin Get_String (Left, S, L); for C in 1 .. Integer'Min (L, Right'Length) loop C2 := Right (Right'First + C - 1); if S (C) < C2 then return -1; elsif S (C) > C2 then return 1; end if; end loop; if L = Right'Length then return 0; elsif L < Right'Length then return -1; else return 1; end if; end Compare; ------------- -- Compare -- ------------- function Compare (Left : XString; Right : XString) return Compare_Result is S : Char_Array; L : Natural; begin Get_String (Right, S, L); return Compare (Left, Char_String (S (1 .. L))); end Compare; ------------------------------ -- Compare_Case_Insensitive -- ------------------------------ function Compare_Case_Insensitive (Left : XString; Right : Char_String) return Compare_Result is S : Char_Array; L : Natural; C2, C3 : Char_Type; begin Get_String (Left, S, L); for C in 1 .. Integer'Min (L, Right'Length) loop C3 := To_Lower (S (C)); C2 := To_Lower (Right (Right'First + C - 1)); if C3 < C2 then return -1; elsif C3 > C2 then return 1; end if; end loop; if L = Right'Length then return 0; elsif L < Right'Length then return -1; else return 1; end if; end Compare_Case_Insensitive; ------------------------------ -- Compare_Case_Insensitive -- ------------------------------ function Compare_Case_Insensitive (Left : XString; Right : XString) return Compare_Result is S : Char_Array; L : Natural; begin Get_String (Right, S, L); return Compare_Case_Insensitive (Left, Char_String (S (1 .. L))); end Compare_Case_Insensitive; --------- -- Get -- --------- function Get (Self : XString; Index : Positive) return Char_Type is B : Char_Array; L : Natural; begin Get_String (Self, B, L); if Index <= L then return B (Index); else raise Ada.Strings.Index_Error with "Invalid index" & Index'Img & " (greater than" & L'Img & ")"; end if; end Get; --------------- -- Reference -- --------------- function Reference (Self : aliased in out XString; Index : Positive) return Character_Reference is B : Char_Array; L : Natural; begin if Copy_On_Write and then Self.Data.Big.Is_Big then -- Make the string unshareable Make_Writable (Self); Self.Data.Big.Data.Refcount := Unshareable; end if; Get_String (Self, B, L); if Index <= L then return (Char => B (Index)'Unrestricted_Access); else raise Ada.Strings.Index_Error with "Invalid index" & Index'Img & " (greater than" & L'Img & ")"; end if; end Reference; ----------- -- Slice -- ----------- procedure Slice (Self : in out XString; Low : Positive; High : Natural) is New_Size : Natural; begin if Low > High then Self.Clear; return; end if; New_Size := High - Low + 1; if not Self.Data.Small.Is_Big then if Low > Natural (Self.Data.Small.Size) or else High > Natural (Self.Data.Small.Size) then raise Ada.Strings.Index_Error; end if; Self.Data.Small.Data (1 .. New_Size) := Self.Data.Small.Data (Low .. High); Self.Data.Small.Size := SSize (New_Size); else if String_Size (Low) > Self.Data.Big.Size or else String_Size (High) > Self.Data.Big.Size then raise Ada.Strings.Index_Error; end if; -- Keep the same data (no need for change in refcount -- or to duplicate) Self.Data.Big.First := Low + Self.Data.Big.First - 1; Self.Data.Big.Size := String_Size (New_Size); end if; end Slice; ----------- -- Slice -- ----------- procedure Slice (Self : XString; Low : Positive; High : Natural; Into : in out XString) is Len : constant Natural := Self.Length; Size : String_Size; Is_Same : Boolean; Str, IStr : Char_Array; begin -- Match the behavior of standard strings if Low > High then Into.Clear; return; end if; -- We can't use Set, since we want to share the buffer when -- possible. if Low > Len then raise Ada.Strings.Index_Error with Low'Img & ">" & Len'Img; end if; if High > Len then raise Ada.Strings.Index_Error with High'Img & ">" & Len'Img; end if; -- We should not call Reserve: this would call Make_Writable, -- and thus potentially requires a copy of the buffer. Instead, -- we want to reuse the buffer if possible. -- But Into might already have some data, so we must avoid leaks Size := String_Size (High - Low + 1); if not Self.Data.Big.Is_Big then -- Taking a slice of a small string always results in small if Into.Data.Big.Is_Big then Finalize (Into); Into.Data.Big.Is_Big := False; end if; Into.Data.Small.Data (1 .. Integer (Size)) := Self.Data.Small.Data (Low .. High); Into.Data.Small.Size := SSize (Size); elsif Copy_On_Write and then Self.Data.Big.Data.Refcount /= Unshareable then Is_Same := Into.Data.Big.Is_Big and then Into.Data.Big.Data = Self.Data.Big.Data; -- Stop holding a shared buffer, if we were if not Is_Same then Finalize (Into); end if; Into.Data.Big := (Is_Big => True, Data => Self.Data.Big.Data, Half_Capacity => Self.Data.Big.Half_Capacity, Size => Size, First => Low + Self.Data.Big.First - 1); if not Is_Same then Increment (Into.Data.Big.Data.Refcount); -- buffer is shared end if; else -- If Into and Self are the same object (the only case where -- their Data is the same), keep that buffer and change the -- slice we use. if Into.Data.Big.Is_Big and then Into.Data.Big.Data = Self.Data.Big.Data then Into.Data.Big.First := Low + Self.Data.Big.First - 1; Into.Data.Big.Size := Size; else if Copy_On_Write then Str := Convert (Self.Data.Big.Data.Bytes2'Address); else Str := Convert (Self.Data.Big.Data.Bytes1'Address); end if; -- Try and reuse memory if we can. This memory is unique -- to Into, so we can safely alter it. if not Into.Data.Big.Is_Big then if Size <= Max_Small_Length then Into.Data.Small.Data (1 .. Natural (Size)) := Char_String (Str (Low + Self.Data.Big.First - 1 .. High + Self.Data.Big.First - 1)); Into.Data.Small.Size := SSize (Size); return; else Into.Data.Small.Size := 0; Convert_To_Big_String (Into, Size); end if; else Into.Data.Big.Size := 0; Reserve (Into, Capacity => Size); end if; if Copy_On_Write then IStr := Convert (Into.Data.Big.Data.Bytes2'Address); else IStr := Convert (Into.Data.Big.Data.Bytes1'Address); end if; Into.Data.Big.Size := Size; IStr (Into.Data.Big.First .. Into.Data.Big.First + Natural (Size) - 1) := Str (Low + Self.Data.Big.First - 1 .. High + Self.Data.Big.First - 1); end if; end if; end Slice; ----------- -- Slice -- ----------- function Slice (Self : XString; Low : Positive; High : Natural) return XString is Result : XString; begin Slice (Self, Low, High, Into => Result); return Result; end Slice; ---------- -- Trim -- ---------- procedure Trim (Self : in out XString; Side : Ada.Strings.Trim_End := Ada.Strings.Both; Chars : Char_Type := Space) is S : Char_Array; L : Natural; F : Natural := 1; begin Get_String (Self, S, L); if Side = Ada.Strings.Both or else Side = Ada.Strings.Right then while L >= 1 and then S (L) = Chars loop L := L - 1; end loop; end if; if Side = Ada.Strings.Both or else Side = Ada.Strings.Left then while F <= L and then S (F) = Chars loop F := F + 1; end loop; end if; Self.Slice (F, L); end Trim; ---------- -- Trim -- ---------- function Trim (Self : XString; Side : Ada.Strings.Trim_End := Ada.Strings.Both; Chars : Char_Type := Space) return XString is S : Char_Array; L : Natural; F : Natural := 1; begin Get_String (Self, S, L); if Side = Ada.Strings.Both or else Side = Ada.Strings.Right then while L >= 1 and then S (L) = Chars loop L := L - 1; end loop; end if; if Side = Ada.Strings.Both or else Side = Ada.Strings.Left then while F <= L and then S (F) = Chars loop F := F + 1; end loop; end if; return Self.Slice (F, L); end Trim; ----------------- -- Starts_With -- ----------------- function Starts_With (Self : XString; Prefix : Char_String) return Boolean is S : Char_Array; L : Natural; begin Get_String (Self, S, L); return L >= Prefix'Length and then Char_String (S (1 .. Prefix'Length)) = Prefix; end Starts_With; ----------------- -- Starts_With -- ----------------- function Starts_With (Self : XString; Prefix : XString) return Boolean is S, S2 : Char_Array; L, L2 : Natural; begin Get_String (Self, S, L); Get_String (Prefix, S2, L2); return L >= L2 and then S (1 .. L2) = S2 (1 .. L2); end Starts_With; --------------- -- Ends_With -- --------------- function Ends_With (Self : XString; Suffix : Char_String) return Boolean is S : Char_Array; L : Natural; begin Get_String (Self, S, L); return L >= Suffix'Length and then Char_String (S (L - Suffix'Length + 1 .. L)) = Suffix; end Ends_With; --------------- -- Ends_With -- --------------- function Ends_With (Self : XString; Suffix : XString) return Boolean is S, S2 : Char_Array; L, L2 : Natural; begin Get_String (Self, S, L); Get_String (Suffix, S2, L2); return L >= L2 and then S (L - L2 + 1 .. L) = S2 (1 .. L2); end Ends_With; ---------- -- Head -- ---------- function Head (Self : XString; Count : Natural) return XString is L : constant Natural := Self.Length; begin return Self.Slice (1, Natural'Min (Count, L)); end Head; ---------- -- Tail -- ---------- function Tail (Self : XString; Count : Natural) return XString is L : constant Natural := Self.Length; begin return Self.Slice (Natural'Max (1, L - Count + 1), L); end Tail; ------------- -- Replace -- ------------- procedure Replace (Self : in out XString; Index : Positive; Char : Char_Type) is S : Char_Array; L : Natural; begin if Self.Data.Big.Is_Big then Make_Writable (Self); end if; Get_String (Self, S, L); if Index > L then raise Ada.Strings.Index_Error with Index'Img & ">" & L'Img; end if; S (Index) := Char; end Replace; ------------- -- Replace -- ------------- procedure Replace (Self : in out XString; Low : Positive; High : Natural; By : Char_String) is S : Char_Array; L, L2 : Natural; New_L : Natural; begin L := Self.Length; if Low > L then raise Ada.Strings.Index_Error with Low'Img & ">" & L'Img; end if; if High >= L then New_L := Low - 1 + By'Length; else New_L := Low - 1 + By'Length + (L - High); end if; -- This makes the string writable Self.Reserve (String_Size (New_L)); -- Couldn't get the string before, since we might have reset it Get_String (Self, S, L2); if High < L then S (Low + By'Length .. Low + By'Length + L - High - 1) := S (High + 1 .. L); end if; if By'Length /= 0 then S (Low .. Low + By'Length - 1) := Convert (By'Address) (1 .. By'Length); end if; if Self.Data.Small.Is_Big then Self.Data.Big.Size := String_Size (New_L); else Self.Data.Small.Size := SSize (New_L); end if; end Replace; ------------------- -- Replace_Slice -- ------------------- procedure Replace_Slice (Self : in out XString; Low : Positive; High : Natural; By : XString) is By_Length : constant Natural := By.Length; S, S2 : Char_Array; L, L2 : Natural; New_L : Natural; begin -- First make strings unique, in case Self and By share a buffer. -- Unfortunately, just calling Make_Writable first would require -- one malloc here, then a second one to reserve the correct size. -- So instead we have to duplicate part of the code for Replace. L := Self.Length; if Low > L then raise Ada.Strings.Index_Error with Low'Img & ">" & L'Img; end if; if High >= L then New_L := Low - 1 + By_Length; else New_L := Low - 1 + By_Length + (L - High); end if; -- This makes the string writable, and ensure we no longer share -- the buffer. Self.Reserve (String_Size (New_L)); -- Couldn't get the string before, since we might have reset it Get_String (Self, S, L2); Get_String (By, S2, L2); if High < L then S (Low + By_Length .. Low + By_Length + L - High - 1) := S (High + 1 .. L); end if; if By_Length /= 0 then S (Low .. Low + By_Length - 1) := S2 (1 .. L2); end if; if Self.Data.Small.Is_Big then Self.Data.Big.Size := String_Size (New_L); else Self.Data.Small.Size := SSize (New_L); end if; end Replace_Slice; ------------ -- Insert -- ------------ procedure Insert (Self : in out XString; Before : Positive; New_Item : Char_String) is begin Self.Replace (Low => Before, High => Before - 1, By => New_Item); end Insert; ------------ -- Insert -- ------------ procedure Insert (Self : in out XString; Before : Positive; New_Item : XString) is begin Self.Replace_Slice (Low => Before, High => Before - 1, By => New_Item); end Insert; --------------- -- Overwrite -- --------------- procedure Overwrite (Self : in out XString; Position : Positive; New_Item : Char_String) is begin Self.Replace (Low => Position, High => Position + New_Item'Length - 1, By => New_Item); end Overwrite; --------------- -- Overwrite -- --------------- procedure Overwrite (Self : in out XString; Position : Positive; New_Item : XString) is begin Self.Replace_Slice (Low => Position, High => Position + New_Item.Length - 1, By => New_Item); end Overwrite; ------------ -- Delete -- ------------ procedure Delete (Self : in out XString; Low : Positive; High : Natural) is begin Self.Replace (Low, High, Char_String'(1 .. 0 => Char_Type'First)); end Delete; ---------- -- Hash -- ---------- function Hash (Self : XString) return Ada.Containers.Hash_Type is function H is new System.String_Hash.Hash (Char_Type, Char_String, Ada.Containers.Hash_Type); S : Char_Array; L : Natural; begin Get_String (Self, S, L); return H (Char_String (S (1 .. L))); end Hash; --------------------------- -- Hash_Case_Insensitive -- --------------------------- function Hash_Case_Insensitive (Self : XString) return Ada.Containers.Hash_Type is function H is new System.String_Hash.Hash (Char_Type, Char_String, Ada.Containers.Hash_Type); S : Char_Array; L : Natural; begin Get_String (Self, S, L); declare S2 : Char_String := Char_String (S (1 .. L)); begin for C in 1 .. L loop S2 (C) := To_Lower (S2 (C)); end loop; return H (S2); end; end Hash_Case_Insensitive; ---------- -- Swap -- ---------- procedure Swap (Self, Str : in out XString) is D : constant String_Data := Str.Data; begin Str.Data := Self.Data; Self.Data := D; end Swap; ------------ -- Center -- ------------ procedure Center (Self : in out XString; Width : Positive; Pad : Char_Type := Space) is Len : constant Natural := Self.Length; S : Char_Array; L : Natural; F : Positive; begin if Len < Width then Self.Reserve (String_Size (Width)); Get_String (Self, S, L); F := (Width - Len + 1) / 2; S (F + 1 .. F + L) := S (1 .. L); for C in 1 .. F loop S (C) := Pad; end loop; for C in F + L + 1 .. Width loop S (C) := Pad; end loop; Store_Size (Self, Width); end if; end Center; ------------ -- Center -- ------------ function Center (Self : XString; Width : Positive; Pad : Char_Type := Space) return XString is Len : constant Natural := Self.Length; Result : XString; F : Positive; S, S2 : Char_Array; L, L2 : Natural; begin if Len >= Width then return Self; else Result.Reserve (String_Size (Width)); Get_String (Self, S, L); Get_String (Result, S2, L2); F := (Width - Len + 1) / 2; for C in 1 .. F loop S2 (C) := Pad; end loop; S2 (F + 1 .. F + L) := S (1 .. L); for C in F + L + 1 .. Width loop S2 (C) := Pad; end loop; Store_Size (Result, Width); return Result; end if; end Center; ------------------ -- Left_Justify -- ------------------ procedure Left_Justify (Self : in out XString; Width : Positive; Pad : Char_Type := Space) is Len : constant Natural := Self.Length; S : Char_Array; L : Natural; begin if Len < Width then Self.Reserve (String_Size (Width)); Get_String (Self, S, L); for C in Len + 1 .. Width loop S (C) := Pad; end loop; Store_Size (Self, Width); end if; end Left_Justify; ------------------ -- Left_Justify -- ------------------ function Left_Justify (Self : XString; Width : Positive; Pad : Char_Type := Space) return XString is -- A simpler implementation is: -- Result : XString := Self; -- Result.Left_Justify (Width, Pad); -- But when not using copy-on-write this results in onre -- extra copy of the string. Len : constant Natural := Self.Length; S : Char_Array; L : Natural; Result : XString; begin if Len >= Width then return Self; else Result := Self; Result.Reserve (String_Size (Width)); Get_String (Result, S, L); for C in Len + 1 .. Width loop S (C) := Pad; end loop; Store_Size (Result, Width); return Result; end if; end Left_Justify; ------------------- -- Right_Justify -- ------------------- procedure Right_Justify (Self : in out XString; Width : Positive; Pad : Char_Type := Space) is Len : constant Natural := Self.Length; S : Char_Array; L : Natural; begin if Len < Width then Self.Reserve (String_Size (Width)); Get_String (Self, S, L); S (Width - Len + 1 .. Width) := S (1 .. Len); for C in 1 .. Width - Len loop S (C) := Pad; end loop; Store_Size (Self, Width); end if; end Right_Justify; ------------------- -- Right_Justify -- ------------------- function Right_Justify (Self : XString; Width : Positive; Pad : Char_Type := Space) return XString is Len : constant Natural := Self.Length; S, S2 : Char_Array; L, L2 : Natural; Result : XString; begin if Len >= Width then return Self; else Result.Reserve (String_Size (Width)); Get_String (Result, S, L); Get_String (Self, S2, L2); S (Width - Len + 1 .. Width) := S2 (1 .. Len); for C in 1 .. Width - L2 loop S (C) := Pad; end loop; Store_Size (Result, Width); return Result; end if; end Right_Justify; ----------- -- Count -- ----------- function Count (Self : XString; Char : Char_Type; Low : Positive := 1; High : Natural := Natural'Last) return Natural is S : Char_Array; L : Natural; Result : Natural := 0; begin Get_String (Self, S, L); if L = 0 then return 0; end if; if Low > L then raise Ada.Strings.Index_Error with Low'Img & " >" & L'Img; end if; L := Natural'Min (High, L); for C in Low .. L loop if S (C) = Char then Result := Result + 1; end if; end loop; return Result; end Count; ----------- -- Count -- ----------- function Count (Self : XString; Str : Char_String; Low : Positive := 1; High : Natural := Natural'Last) return Natural is SL : constant Integer := Str'Length - 1; S : Char_Array; L : Natural; Num : Natural := 0; Index : Natural := Low; begin Get_String (Self, S, L); if L = 0 then return 0; end if; if SL = -1 then return Natural'Last; end if; if Low > L then raise Ada.Strings.Index_Error with Low'Img & " >" & L'Img; end if; L := Natural'Min (High, L); while Index <= L - SL loop if Char_String (S (Index .. Index + SL)) = Str then Num := Num + 1; Index := Index + SL + 1; else Index := Index + 1; end if; end loop; return Num; end Count; ---------- -- Find -- ---------- function Find (Self : XString; Char : Char_Type; Low : Positive := 1; High : Natural := Natural'Last) return Natural is S : Char_Array; L : Natural; begin Get_String (Self, S, L); if Low > L then raise Ada.Strings.Index_Error with Low'Img & " >" & L'Img; end if; L := Natural'Min (High, L); for C in Low .. L loop if S (C) = Char then return C; end if; end loop; return 0; end Find; ---------- -- Find -- ---------- function Find (Self : XString; Str : Char_String; Low : Positive := 1; High : Natural := Natural'Last) return Natural is SL : constant Integer := Str'Length - 1; S : Char_Array; L : Natural; Index : Natural := Low; begin Get_String (Self, S, L); if L = 0 or else SL = -1 then return 0; end if; if Low > L then raise Ada.Strings.Index_Error with Low'Img & " >" & L'Img; end if; L := Natural'Min (High, L); while Index <= L - SL loop if Char_String (S (Index .. Index + SL)) = Str then return Index; end if; Index := Index + 1; end loop; return 0; end Find; ---------------- -- Right_Find -- ---------------- function Right_Find (Self : XString; Char : Char_Type; Low : Positive := 1; High : Natural := Natural'Last) return Natural is S : Char_Array; L : Natural; begin Get_String (Self, S, L); if Low > L then raise Ada.Strings.Index_Error with Low'Img & " >" & L'Img; end if; L := Natural'Min (High, L); for C in reverse Low .. L loop if S (C) = Char then return C; end if; end loop; return 0; end Right_Find; ---------------- -- Right_Find -- ---------------- function Right_Find (Self : XString; Str : Char_String; Low : Positive := 1; High : Natural := Natural'Last) return Natural is SL : constant Integer := Str'Length - 1; S : Char_Array; L : Natural; Index : Natural; begin Get_String (Self, S, L); if L = 0 or else SL = -1 then return 0; end if; if Low > L then raise Ada.Strings.Index_Error with Low'Img & " >" & L'Img; end if; L := Natural'Min (High, L); Index := L - SL; while Index >= 1 loop if Char_String (S (Index .. Index + SL)) = Str then return Index; end if; Index := Index - 1; end loop; return 0; end Right_Find; ----------- -- Split -- ----------- procedure Split (Self : XString; Sep : Char_Type; Omit_Empty : Boolean := False; Into : out XString_Array; Last : out Natural) is S : Char_Array; L : Natural; Index : Positive; F : Positive; -- Start of current chunk begin Get_String (Self, S, L); if Into'Length = 1 then if L = 0 and then Omit_Empty then Last := Into'First - 1; else Last := Into'First; Into (Last) := Self; end if; else Last := Into'First - 1; Index := 1; F := 1; while Index <= L loop -- For efficiency, we do not use Find here, since that would -- do a lot of extra testing that we do not need if S (Index) = Sep then if not Omit_Empty or else F <= Index - 1 then Last := Last + 1; Slice (Self, F, Index - 1, Into => Into (Last)); end if; F := Index + 1; exit when Last = Into'Last - 1; end if; Index := Index + 1; end loop; if F > L then if not Omit_Empty then Last := Last + 1; Into (Last).Clear; end if; else Last := Last + 1; Slice (Self, F, L, Into => Into (Last)); end if; end if; end Split; ----------- -- Split -- ----------- function Split (Self : XString; Sep : Char_Type; Max_Split : Positive := Positive'Last; Omit_Empty : Boolean := False) return XString_Array is L : constant Natural := Self.Length; Max_Size : constant Natural := (if Max_Split /= Natural'Last then Integer'Min (Max_Split, L) else Self.Count (Sep) + 1); Result : XString_Array (1 .. Max_Size); Last : Natural; begin Split (Self, Sep, Omit_Empty, Result, Last); return Result (Result'First .. Last); end Split; ----------- -- Split -- ----------- function Split (Self : XString; Sep : Char_String; Max_Split : Positive := Positive'Last; Omit_Empty : Boolean := False) return XString_Array is L : constant Natural := Self.Length; Max_Size : constant Natural := (if Sep'Length = 0 then 1 -- Won't be used anyway elsif Max_Split /= Natural'Last then Integer'Min (Max_Split, L) else Self.Count (Sep) + 1); Result : XString_Array (1 .. Max_Size); Last : Natural; begin Split (Self, Sep, Omit_Empty, Result, Last); return Result (Result'First .. Last); end Split; ----------- -- Split -- ----------- procedure Split (Self : XString; Sep : Char_String; Omit_Empty : Boolean := False; Into : out XString_Array; Last : out Natural) is SL : constant Integer := Sep'Length - 1; S : Char_Array; L : Natural; Index : Positive; F : Positive; -- Start of current chunk begin Get_String (Self, S, L); if L = 0 or else SL = -1 then Last := Into'First - 1; return; end if; if Into'Length = 1 then if L = 0 and then Omit_Empty then Last := Into'First - 1; else Last := Into'First; Into (Last) := Self; end if; else Last := Into'First - 1; Index := 1; F := 1; while Index <= L - SL loop -- For efficiency, we do not use Find here, since that would -- do a lot of extra testing that we do not need if Char_String (S (Index .. Index + SL)) = Sep then if not Omit_Empty or else F <= Index - 1 then Last := Last + 1; Slice (Self, F, Index - 1, Into => Into (Last)); end if; F := Index + SL + 1; exit when Last = Into'Last - 1; Index := F; else Index := Index + 1; end if; end loop; if F > L then if not Omit_Empty then Last := Last + 1; Into (Last).Clear; end if; else Last := Last + 1; Slice (Self, F, L, Into => Into (Last)); end if; end if; end Split; ----------------- -- Right_Split -- ----------------- procedure Right_Split (Self : XString; Sep : Char_Type; Omit_Empty : Boolean := False; Into : out XString_Array; Last : out Natural) is S : Char_Array; L : Natural; Index : Integer; F : Natural; -- End of current chunk begin Get_String (Self, S, L); if Into'Length = 1 then if L = 0 and then Omit_Empty then Last := Into'First - 1; else Last := Into'First; Into (Last) := Self; end if; else Last := Into'First - 1; Index := L; F := L; while Index >= 1 loop if S (Index) = Sep then if not Omit_Empty or else F > Index then Last := Last + 1; if Index >= L then Into (Last).Clear; else Slice (Self, Index + 1, F, Into => Into (Last)); end if; end if; F := Index - 1; exit when Last = Into'Last - 1; end if; Index := Index - 1; end loop; if F < 1 then if not Omit_Empty then Last := Last + 1; Into (Last).Clear; end if; else Last := Last + 1; Slice (Self, 1, F, Into => Into (Last)); end if; end if; end Right_Split; ----------------- -- Right_Split -- ----------------- function Right_Split (Self : XString; Sep : Char_Type; Max_Split : Positive := Positive'Last; Omit_Empty : Boolean := False) return XString_Array is L : constant Natural := Self.Length; Max_Size : constant Natural := (if Max_Split /= Natural'Last then Integer'Min (Max_Split, L) else Self.Count (Sep) + 1); Result : XString_Array (1 .. Max_Size); Last : Natural; begin Right_Split (Self, Sep, Omit_Empty, Result, Last); return Result (Result'First .. Last); end Right_Split; ----------------- -- Right_Split -- ----------------- procedure Right_Split (Self : XString; Sep : Char_String; Omit_Empty : Boolean := False; Into : out XString_Array; Last : out Natural) is SL : constant Integer := Sep'Length - 1; S : Char_Array; L : Natural; Index : Integer; F : Natural; -- End of current chunk begin Get_String (Self, S, L); if L = 0 or else SL = -1 then Last := Into'First - 1; return; end if; if Into'Length = 1 then if L = 0 and then Omit_Empty then Last := Into'First - 1; else Last := Into'First; Into (Last) := Self; end if; else Last := Into'First - 1; Index := L - SL; F := L; while Index >= 1 loop if Char_String (S (Index .. Index + SL)) = Sep then if not Omit_Empty or else Index + SL + 1 <= F then Last := Last + 1; if Index + SL + 1 > L then Into (Last).Clear; else Slice (Self, Index + SL + 1, F, Into => Into (Last)); end if; end if; F := Index - 1; exit when Last = Into'Last - 1; Index := Index - SL; end if; Index := Index - 1; end loop; if F < 1 then if not Omit_Empty then Last := Last + 1; Into (Last).Clear; end if; else Last := Last + 1; Slice (Self, 1, F, Into => Into (Last)); end if; end if; end Right_Split; ----------------- -- Right_Split -- ----------------- function Right_Split (Self : XString; Sep : Char_String; Max_Split : Positive := Positive'Last; Omit_Empty : Boolean := False) return XString_Array is L : constant Natural := Self.Length; Max_Size : constant Natural := (if Sep'Length = 0 then 1 -- Won't be used anyway elsif Max_Split /= Natural'Last then Integer'Min (Max_Split, L) else Self.Count (Sep) + 1); Result : XString_Array (1 .. Max_Size); Last : Natural; begin Right_Split (Self, Sep, Omit_Empty, Result, Last); return Result (Result'First .. Last); end Right_Split; ---------- -- Join -- ---------- function Join (Sep : Char_Type; Items : XString_Array; Prefix : Char_String := Null_Char_String; Suffix : Char_String := Null_Char_String) return XString is Result : XString; begin Result.Set_As_Join (Sep, Items, Prefix, Suffix); return Result; end Join; ---------- -- Join -- ---------- function Join (Sep : XString; Items : XString_Array; Prefix : Char_String := Null_Char_String; Suffix : Char_String := Null_Char_String) return XString is Result : XString; B : Char_Array; L : Natural; begin Get_String (Sep, B, L); Result.Set_As_Join (Char_String (B (1 .. L)), Items, Prefix, Suffix); return Result; end Join; ----------------- -- Set_As_Join -- ----------------- procedure Set_As_Join (Self : out XString; Sep : Char_Type; Items : XString_Array; Prefix : Char_String := Null_Char_String; Suffix : Char_String := Null_Char_String) is Size : Integer; begin if Items'Length = 0 then Self.Set (Prefix); Self.Append (Suffix); else Size := Prefix'Length + Suffix'Length + Items'Length - 1; -- size for separators for It of Items loop Size := Size + It.Length; end loop; Store_Size (Self, 0); -- Reset the string Self.Reserve (String_Size (Size)); Self.Append (Prefix); for It in Items'Range loop Self.Append (Items (It)); if It /= Items'Last then Self.Append (Sep); end if; end loop; Self.Append (Suffix); end if; end Set_As_Join; ---------- -- Join -- ---------- function Join (Sep : Char_String; Items : XString_Array; Prefix : Char_String := Null_Char_String; Suffix : Char_String := Null_Char_String) return XString is Result : XString; begin Result.Set_As_Join (Sep, Items, Prefix, Suffix); return Result; end Join; ----------------- -- Set_As_Join -- ----------------- procedure Set_As_Join (Self : out XString; Sep : Char_String; Items : XString_Array; Prefix : Char_String := Null_Char_String; Suffix : Char_String := Null_Char_String) is Size : Natural; begin if Items'Length = 0 then Self.Set (Prefix); Self.Append (Suffix); else Size := Sep'Length * (Items'Length - 1) + Prefix'Length + Suffix'Length; for It of Items loop Size := Size + It.Length; end loop; Store_Size (Self, 0); -- Reset the string Self.Reserve (String_Size (Size)); Self.Append (Prefix); for It in Items'Range loop Self.Append (Items (It)); if It /= Items'Last then Self.Append (Sep); end if; end loop; Self.Append (Suffix); end if; end Set_As_Join; -------------- -- To_Upper -- -------------- procedure To_Upper (Self : in out XString) is S : Char_Array; L : Natural; begin Make_Writable (Self); Get_String (Self, S, L); for Idx in 1 .. L loop S (Idx) := To_Upper (S (Idx)); end loop; end To_Upper; -------------- -- To_Upper -- -------------- function To_Upper (Self : XString) return XString is R : XString := Self; begin To_Upper (R); return R; end To_Upper; -------------- -- To_Lower -- -------------- procedure To_Lower (Self : in out XString) is S : Char_Array; L : Natural; begin Make_Writable (Self); Get_String (Self, S, L); for Idx in 1 .. L loop S (Idx) := To_Lower (S (Idx)); end loop; end To_Lower; -------------- -- To_Lower -- -------------- function To_Lower (Self : XString) return XString is R : XString := Self; begin To_Lower (R); return R; end To_Lower; ---------------- -- Capitalize -- ---------------- procedure Capitalize (Self : in out XString) is S : Char_Array; L : Natural; begin Make_Writable (Self); Get_String (Self, S, L); S (1) := To_Upper (S (1)); for Idx in 2 .. L loop S (Idx) := To_Lower (S (Idx)); end loop; end Capitalize; ----------- -- Title -- ----------- procedure Title (Self : in out XString) is S : Char_Array; L : Natural; Idx : Natural; begin Make_Writable (Self); Get_String (Self, S, L); S (1) := To_Upper (S (1)); Idx := 2; while Idx < L loop if S (Idx) = Space then S (Idx + 1) := To_Upper (S (Idx + 1)); Idx := Idx + 2; else Idx := Idx + 1; end if; end loop; end Title; -------------- -- Is_Upper -- -------------- function Is_Upper (Self : XString) return Boolean is begin for C of Self loop if C /= To_Upper (C) then return False; end if; end loop; return True; end Is_Upper; -------------- -- Is_Lower -- -------------- function Is_Lower (Self : XString) return Boolean is begin for C of Self loop if C /= To_Lower (C) then return False; end if; end loop; return True; end Is_Lower; end Strings; end GNATCOLL.Strings_Impl; gnatcoll-core-21.0.0/src/gnatcoll-asserts.ads0000644000175000017500000002234713661715457020761 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2005-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides support for writing assertions in source code, -- while getting proper error messages. -- -- GNAT's "pragma Assert" does not display the expression that was tested, -- so users end up writing the name of the variables in the message string -- itself. This message is then associated with the Assertion_Error exception -- that is raised. But exceptions limit the maximal length of such messages, -- so again it is hard to have a meaningful message. -- -- GNATCOLL.Traces provides an Assert procedure which tests the boolean -- expression and displays a log message if it is false (and optionally -- raises an exception). But the expression still needs to be manually -- described in the message. -- -- This package intends to ease the writing of the error messages, by -- automatically generating messages that include the expression that -- was tested, as well as the source location for that test. -- The result of a failed assertion is left under control of an error -- reporter, which you can customize in your application to raise -- exceptions, log error messages, write to a GUI console, send to a -- socket,... with GNAT.Source_Info; use GNAT.Source_Info; package GNATCOLL.Asserts is -------------------- -- Error_Reporter -- -------------------- type Error_Reporter is interface; -- This type is responsible for reacting when an assertion fails. procedure On_Assertion_Failed (Self : Error_Reporter; Msg : String; Details : String; Location : String; Entity : String) is abstract; -- Report the assertion error. -- The exact behavior depends on your application, and you will in -- general provide your own implementation of Error_Reporter unless -- one of the basic ones provided below works for you. -- Self will often be a global variable, and should therefore be -- stateless as much as possible (if your application is multi-threaded, -- it will be shared among the threads). For this reason, the parameter -- is set as "in". ------------------------ -- Exception_Reporter -- ------------------------ type Exception_Reporter is new Error_Reporter with null record; overriding procedure On_Assertion_Failed (Self : Exception_Reporter; Msg : String; Details : String; Location : String; Entity : String); -- An error reporter that simply raises an Assertion_Error and sets a -- message for it. Due to limitations in the implementation of -- exceptions, the length of the message is limited, and thus Msg will -- be truncated as needed. ---------------- -- Assertions -- ---------------- -- The following packages provides multiple ways to test values. These -- are implemented as generic packages so that they can also be applied -- to user-defined types. -- All the Assert_* procedures receives some common parameters: -- Msg : String -- Is an extra message that should be part of the error message, -- and can be used to provide additional explanations. For -- efficiency, this should be a static string as much as possible, -- since the compiler will always need to build that string to -- pass it to Assert_*, even if the latter does not use it in the -- end. -- Location, Entity -- These are used to compute statically the source location of -- the failed assertion. You should not provide actual values for -- these. generic Report : Error_Reporter'Class; -- The error reporter used by all Assert_* procedures below. This -- will in general be a global variable. Enabled : Boolean := True; -- If you set this to False, none of the Assertions will perform any -- work, and they might be omitted from the final binary altogether. package Asserts is procedure Assert_Failed (Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); -- Unconditionally report an error. generic type T (<>) is limited private; with function Image (V : T) return String is <>; with function "=" (Left, Right : T) return Boolean is <>; package Equals is procedure Assert_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); procedure Assert (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) renames Assert_Equal; -- Left must be equal to Right, using the "=" operator. procedure Assert_Not_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); -- Left must be different from Right, using the "=" operator. end Equals; generic type T (<>) is limited private; with function Image (V : T) return String is <>; with function "=" (Left, Right : T) return Boolean is <>; with function "<" (Left, Right : T) return Boolean is <>; package Compare is procedure Assert_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); procedure Assert (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) renames Assert_Equal; -- Left must be equal to Right, using the "=" operator. procedure Assert_Not_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); -- Left must be different from Right, using the "=" operator. procedure Assert_Less (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); -- asserts Left < Right procedure Assert_Less_Or_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); -- asserts Left <= Right procedure Assert_Greater_Or_Equal (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); -- asserts Left >= Right procedure Assert_Greater (Left, Right : T; Msg : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); -- asserts Left > Right end Compare; end Asserts; end GNATCOLL.Asserts; gnatcoll-core-21.0.0/src/gnatcoll-scripts-files.ads0000644000175000017500000000601213661715457022053 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- Implementation of File class with GNATCOLL.Scripts; use GNATCOLL.Scripts; with GNATCOLL.VFS; package GNATCOLL.Scripts.Files is procedure Register_Commands (Repo : access Scripts_Repository_Record'Class); -- Add basic script commands for File class. -- Next subprogram could be useful to define new commands function Get_File_Class (Repo : access Scripts_Repository_Record'Class) return Class_Type; -- Return the class to use for file types. This encapsulates a File_Info. function Nth_Arg (Data : Callback_Data'Class; N : Positive) return GNATCOLL.VFS.Virtual_File; procedure Set_Nth_Arg (Data : in out Callback_Data'Class; N : Positive; File : GNATCOLL.VFS.Virtual_File); function Get_Data (Instance : Class_Instance) return GNATCOLL.VFS.Virtual_File; procedure Set_Data (Instance : Class_Instance; File : Virtual_File); -- Retrieve the file information from an instance. This returns No_File -- if no instance is passed function Create_File (Script : access Scripting_Language_Record'Class; File : GNATCOLL.VFS.Virtual_File) return Class_Instance; -- Return a new File function Get_File_Class (Data : Callback_Data'Class) return Class_Type; -- Return Class_Type from File class end GNATCOLL.Scripts.Files; gnatcoll-core-21.0.0/src/gnatcoll-coders.ads0000644000175000017500000001541713661715457020554 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package declares the interface for different types of coders, such -- as compressors/decompressors, encoders/decoders, and any other kind of -- streaming data transformation. with Ada.Streams; use Ada.Streams; package GNATCOLL.Coders is type Coder_Interface is limited interface; type Coder_Access is access all Coder_Interface'Class; type Flush_Mode is (No_Flush, -- Regular way for coding, no flush Sync_Flush, -- All pending output is flushed to the output buffer and the output -- is aligned on a byte boundary, so that the decoder can get all -- input data available so far. (In particular In_Last = In_Data'Last -- after the call to Transcode if enough output space has been provided -- before the call). Flushing may degrade compression for some -- compression algorithms and so it should be used only when necessary. Full_Flush, -- All output is flushed as with Synch_Flush, and the coding state is -- reset so that decoding can restart from this point if previous -- compressed data has been damaged or if random access is desired. -- Using Full_Flush too often can seriously degrade the compression kind -- of coding. Finish); -- Just to tell the coder that input data is complete function Is_Open (Coder : Coder_Interface) return Boolean is abstract; -- Indicates that coder is ready to transcode data. -- Initialization procedure has to be implemented in the descendant with -- parameters appropriate to the transcoding algorithm. procedure Transcode (Coder : in out Coder_Interface; In_Data : Stream_Element_Array; In_Last : out Stream_Element_Offset; Out_Data : out Stream_Element_Array; Out_Last : out Stream_Element_Offset; Flush : Flush_Mode) is abstract with Pre'Class => In_Data'First > Stream_Element_Offset'First and then Out_Data'First > Stream_Element_Offset'First; -- Transcodes data from In_Data to Out_Date. -- In_Last is the index of last element from In_Data accepted by -- the Coder. -- Out_Last is the index of the last element written to the Out_Data. -- To tell the Coder that incoming data is complete, pass Finish as the -- Flush parameter and call Transcoder with empty In_Data until Finished -- routine indicates end of stream. procedure Flush (Coder : in out Coder_Interface'Class; Out_Data : out Stream_Element_Array; Out_Last : out Stream_Element_Offset; Flush : Flush_Mode) with Inline; -- Flushes the data from coder function Total_In (Coder : Coder_Interface) return Stream_Element_Count is abstract; -- Returns total amount of input data sent into the coder function Total_Out (Coder : Coder_Interface) return Stream_Element_Count is abstract; -- Returns total amount of output data taken from the coder function Finished (Coder : Coder_Interface) return Boolean is abstract; -- Indicates that incoming data stream finished and all internally -- processed data is out of coder. procedure Close (Coder : in out Coder_Interface) is abstract; -- Frees all internal coder memory allocations private -------------------------------------------------------------------------- -- Generic Read/Write routines helpers to implement streaming interface -- -------------------------------------------------------------------------- generic with procedure Write (Item : Stream_Element_Array); -- User should provide this routine to accept transcoded data Buffer_Size : Stream_Element_Offset; -- Buffer size for Write user routine procedure Write (Coder : in out Coder_Interface'Class; Item : Stream_Element_Array; Flush : Flush_Mode := No_Flush); -- Transcodes data from Item and put it to the generic parameter procedure -- Write. Output buffer size could be set in Buffer_Size generic parameter. generic with procedure Read (Item : out Stream_Element_Array; Last : out Stream_Element_Offset); -- User should provide data to transcode through this routine Buffer : in out Stream_Element_Array; -- Buffer to keep remaining data from the previous back read Rest_First, Rest_Last : in out Stream_Element_Offset; -- Rest_First have to be initialized to Buffer'Last + 1 -- Rest_Last have to be initialized to Buffer'Last -- before usage. Allow_Read_Some : Boolean := False; -- Is it allowed to return Last < Item'Last before end of data procedure Read (Coder : in out Coder_Interface'Class; Item : out Stream_Element_Array; Last : out Stream_Element_Offset; Flush : Flush_Mode := No_Flush); -- Transcodes data from generic parameter procedure Read to the Item. -- User should provide Buffer and initialized Rest_First, Rest_Last -- indicators. If Allow_Read_Some is True, Read routines could return -- Last < Item'Last only at end of stream. end GNATCOLL.Coders; gnatcoll-core-21.0.0/src/gnatcoll-projects-aux.adb0000644000175000017500000000744013661715457021675 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2002-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GPR.Env; with GPR.Names; use GPR.Names; with GPR.Snames; package body GNATCOLL.Projects.Aux is ------------------- -- To_Project_Id -- ------------------- function To_Project_Id (Project : Projects.Project_Type) return GPR.Project_Id is begin return Project.Data.View; end To_Project_Id; ------------------------- -- Project_Tree_Ref_Of -- ------------------------- function Project_Tree_Ref_Of (Project : Projects.Project_Type) return GPR.Project_Tree_Ref is begin return Tree_View (Project); end Project_Tree_Ref_Of; ----------------------------- -- Create_Ada_Mapping_File -- ----------------------------- function Create_Ada_Mapping_File (Project : Projects.Project_Type) return String is Name : Path_Name_Type; begin GPR.Env.Create_Mapping_File (Project => Project.Data.View, Language => Snames.Name_Ada, In_Tree => Tree_View (Project), Name => Name); if Name = No_Path then return ""; else return Get_Name_String (Name); end if; end Create_Ada_Mapping_File; -------------------------------- -- Create_Config_Pragmas_File -- -------------------------------- function Create_Config_Pragmas_File (Project : Projects.Project_Type) return String is begin GPR.Env.Create_Config_Pragmas_File (For_Project => Project.Data.View, In_Tree => Tree_View (Project)); declare Path : constant Path_Name_Type := Project.Data.View.Config_File_Name; begin if Path = No_Path then return ""; else return Get_Name_String (Path); end if; end; end Create_Config_Pragmas_File; --------------------------- -- Delete_All_Temp_Files -- --------------------------- procedure Delete_All_Temp_Files (Root_Project : Projects.Project_Type) is begin GPR.Delete_All_Temp_Files (Tree_View (Root_Project).Shared); end Delete_All_Temp_Files; end GNATCOLL.Projects.Aux; gnatcoll-core-21.0.0/src/gnatcoll-mmap-system__win32.ads0000644000175000017500000002220513661715457022723 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2007-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with System; -- OS peculiarities abstraction package for Win32 systems. package GNATCOLL.Mmap.System is -- The Win package contains copy of definition found in recent System.Win32 -- unit provided with the GNAT compiler. The copy is needed to be able to -- compile this unit with older compilers. Note that this internal Win -- package can be removed when GNAT 6.1.0 is not supported anymore. package Win is subtype PVOID is Standard.System.Address; type HANDLE is new Interfaces.C.ptrdiff_t; type WORD is new Interfaces.C.unsigned_short; type DWORD is new Interfaces.C.unsigned_long; type LONG is new Interfaces.C.long; type SIZE_T is new Interfaces.C.size_t; type LARGE_INTEGER is new Long_Long_Integer; type BOOL is new Interfaces.C.int; for BOOL'Size use Interfaces.C.int'Size; FALSE : constant := 0; GENERIC_READ : constant := 16#80000000#; GENERIC_WRITE : constant := 16#40000000#; OPEN_EXISTING : constant := 3; type OVERLAPPED is record Internal : DWORD; InternalHigh : DWORD; Offset : DWORD; OffsetHigh : DWORD; hEvent : HANDLE; end record; type SECURITY_ATTRIBUTES is record nLength : DWORD; pSecurityDescriptor : PVOID; bInheritHandle : BOOL; end record; type SYSTEM_INFO is record dwOemId : DWORD; dwPageSize : DWORD; lpMinimumApplicationAddress : PVOID; lpMaximumApplicationAddress : PVOID; dwActiveProcessorMask : PVOID; dwNumberOfProcessors : DWORD; dwProcessorType : DWORD; dwAllocationGranularity : DWORD; wProcessorLevel : WORD; wProcessorRevision : WORD; end record; type LP_SYSTEM_INFO is access all SYSTEM_INFO; INVALID_HANDLE_VALUE : constant HANDLE := -1; FILE_BEGIN : constant := 0; FILE_SHARE_READ : constant := 16#00000001#; FILE_ATTRIBUTE_NORMAL : constant := 16#00000080#; FILE_MAP_COPY : constant := 1; FILE_MAP_READ : constant := 4; FILE_MAP_WRITE : constant := 2; PAGE_READONLY : constant := 16#0002#; PAGE_READWRITE : constant := 16#0004#; INVALID_FILE_SIZE : constant := 16#FFFFFFFF#; function CreateFile (lpFileName : Standard.System.Address; dwDesiredAccess : DWORD; dwShareMode : DWORD; lpSecurityAttributes : access SECURITY_ATTRIBUTES; dwCreationDisposition : DWORD; dwFlagsAndAttributes : DWORD; hTemplateFile : HANDLE) return HANDLE; pragma Import (Stdcall, CreateFile, "CreateFileW"); function WriteFile (hFile : HANDLE; lpBuffer : Standard.System.Address; nNumberOfBytesToWrite : DWORD; lpNumberOfBytesWritten : access DWORD; lpOverlapped : access OVERLAPPED) return BOOL; pragma Import (Stdcall, WriteFile, "WriteFile"); function ReadFile (hFile : HANDLE; lpBuffer : Standard.System.Address; nNumberOfBytesToRead : DWORD; lpNumberOfBytesRead : access DWORD; lpOverlapped : access OVERLAPPED) return BOOL; pragma Import (Stdcall, ReadFile, "ReadFile"); function CloseHandle (hObject : HANDLE) return BOOL; pragma Import (Stdcall, CloseHandle, "CloseHandle"); function GetFileSizeEx (hFile : HANDLE; lpFileSizeHigh : access LARGE_INTEGER) return BOOL; pragma Import (Stdcall, GetFileSizeEx, "GetFileSizeEx"); function SetFilePointer (hFile : HANDLE; lDistanceToMove : LONG; lpDistanceToMoveHigh : access LONG; dwMoveMethod : DWORD) return DWORD; pragma Import (Stdcall, SetFilePointer, "SetFilePointer"); function CreateFileMapping (hFile : HANDLE; lpSecurityAttributes : access SECURITY_ATTRIBUTES; flProtect : DWORD; dwMaximumSizeHigh : DWORD; dwMaximumSizeLow : DWORD; lpName : Standard.System.Address) return HANDLE; pragma Import (Stdcall, CreateFileMapping, "CreateFileMappingW"); function MapViewOfFile (hFileMappingObject : HANDLE; dwDesiredAccess : DWORD; dwFileOffsetHigh : DWORD; dwFileOffsetLow : DWORD; dwNumberOfBytesToMap : SIZE_T) return Standard.System.Address; pragma Import (Stdcall, MapViewOfFile, "MapViewOfFile"); function UnmapViewOfFile (lpBaseAddress : Standard.System.Address) return BOOL; pragma Import (Stdcall, UnmapViewOfFile, "UnmapViewOfFile"); procedure GetSystemInfo (lpSystemInfo : LP_SYSTEM_INFO); pragma Import (Stdcall, GetSystemInfo, "GetSystemInfo"); end Win; type System_File is record Handle : Win.HANDLE; Mapped : Boolean; -- Whether mapping is requested by the user and available on the system Mapping_Handle : Win.HANDLE; Write : Boolean; -- Whether this file can be written to Length : File_Size; -- Length of the file. Used to know what can be mapped in the file end record; type System_Mapping is record Address : Standard.System.Address; Length : File_Size; end record; Invalid_System_File : constant System_File := (Win.INVALID_HANDLE_VALUE, False, Win.INVALID_HANDLE_VALUE, False, 0); Invalid_System_Mapping : constant System_Mapping := (Standard.System.Null_Address, 0); function Open_Read (Filename : String; Use_Mmap_If_Available : Boolean := True) return System_File; -- Open a file for reading and return the corresponding System_File. Raise -- a Ada.IO_Exceptions.Name_Error if unsuccessful. function Open_Write (Filename : String; Use_Mmap_If_Available : Boolean := True) return System_File; -- Likewise for writing to a file procedure Close (File : in out System_File); -- Close a system file function Read_From_Disk (File : System_File; Offset, Length : File_Size) return GNAT.Strings.String_Access; -- Read a fragment of a file. It is up to the caller to free the result -- when done with it. procedure Write_To_Disk (File : System_File; Offset, Length : File_Size; Buffer : GNAT.Strings.String_Access); -- Write some content to a fragment of a file procedure Create_Mapping (File : System_File; Offset, Length : in out File_Size; Mutable : Boolean; Mapping : out System_Mapping; Advice : Use_Advice := Use_Normal); -- Create a memory mapping for the given File, for the area starting at -- Offset and containing Length bytes. Store it to Mapping. -- Note that Offset and Length may be modified according to the system -- needs (for boundaries, for instance). The caller must cope with actually -- wider mapped areas. procedure Dispose_Mapping (Mapping : in out System_Mapping); -- Unmap a previously-created mapping function Get_Page_Size return File_Size; -- Return the number of bytes in a system page. end GNATCOLL.Mmap.System; gnatcoll-core-21.0.0/src/gnatcoll-locks.adb0000644000175000017500000000410713661715457020361 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package body GNATCOLL.Locks is ---------------- -- Initialize -- ---------------- procedure Initialize (This : in out Scoped_Lock) is begin This.Lock.Seize; end Initialize; -------------- -- Finalize -- -------------- procedure Finalize (This : in out Scoped_Lock) is begin This.Lock.Release; end Finalize; end GNATCOLL.Locks; gnatcoll-core-21.0.0/src/gnatcoll-io-remote.adb0000644000175000017500000007405513661715457021157 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNAT.Directory_Operations; with GNAT.Strings; use GNAT.Strings; with GNATCOLL.IO.Native; with GNATCOLL.IO.Remote.Unix; with GNATCOLL.IO.Remote.Windows; with GNATCOLL.Mmap; with GNATCOLL.Path; use GNATCOLL.Path; with GNATCOLL.Remote; use GNATCOLL.Remote; with GNATCOLL.Remote.Db; use GNATCOLL.Remote.Db; package body GNATCOLL.IO.Remote is procedure Internal_Initialize (File : not null access Remote_File_Record'Class; Host : String; Path : FS_String); -- Initialize internal fields according to the file's host ------------------------- -- Internal_Initialize -- ------------------------- procedure Internal_Initialize (File : not null access Remote_File_Record'Class; Host : String; Path : FS_String) is Server : constant Server_Access := Get_Server (Host); Last : Natural := Path'Last; begin -- Regexps might return file strings with a trailing CR or LF. Let's -- remove those before creating the File record. while Path (Last) = ASCII.CR or Path (Last) = ASCII.LF loop Last := Last - 1; end loop; File.Server := Server; if File.Tmp_Norm then File.Full := new FS_String' (GNATCOLL.Path.Normalize (Server.Shell_FS, From_Unix (Server.Shell_FS, Path (Path'First .. Last)))); else File.Full := new FS_String' (From_Unix (Server.Shell_FS, Path (Path'First .. Last))); end if; File.Normalized_And_Resolved := null; end Internal_Initialize; ------------------------ -- Ensure_Initialized -- ------------------------ procedure Ensure_Initialized (File : not null access Remote_File_Record'Class) is begin if File.Server /= null then return; elsif not Is_Configured (File.Tmp_Host.all) then raise Remote_Config_Error with "File needs server " & File.Tmp_Host.all & " which is not configured"; end if; Internal_Initialize (File, File.Tmp_Host.all, File.Tmp_Path.all); Free (File.Tmp_Host); Free (File.Tmp_Path); end Ensure_Initialized; ------------ -- Create -- ------------ function Create (Host : String; Path : FS_String; Normalize : Boolean) return File_Access is Ret : Remote_File_Access; begin Ret := new Remote_File_Record' (Ref_Count => 1, Tmp_Host => null, Tmp_Path => null, Tmp_Norm => Normalize, Tmp_Name => (others => ' '), Server => null, Full => null, Normalized => null, Normalized_And_Resolved => null, Kind => Unknown); if not Is_Configured (Host) then -- Delayed initialization Ret.Tmp_Host := new String'(Host); Ret.Tmp_Path := new FS_String'(Path); else Internal_Initialize (Ret, Host, Path); end if; return File_Access (Ret); end Create; ----------------- -- Current_Dir -- ----------------- function Current_Dir (Host : String) return File_Access is Server : Server_Access; begin if not Is_Configured (Host) then raise Remote_Config_Error with "Invalid FS for host " & Host; else Server := Get_Server (Host); end if; case Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return Create (Host, GNATCOLL.IO.Remote.Unix.Current_Dir (Server), False); when FS_Windows => return Create (Host, GNATCOLL.IO.Remote.Windows.Current_Dir (Server), False); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & Host; end case; end Current_Dir; -------------- -- Home_Dir -- -------------- function Home_Dir (Host : String) return File_Access is Server : Server_Access; begin if not Is_Configured (Host) then raise Remote_Config_Error with "Invalid FS for host " & Host; else Server := Get_Server (Host); end if; case Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return Create (Host, GNATCOLL.IO.Remote.Unix.Home_Dir (Server), False); when FS_Windows => return Create (Host, GNATCOLL.IO.Remote.Windows.Home_Dir (Server), False); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & Host; end case; end Home_Dir; ----------------------- -- Get_Tmp_Directory -- ----------------------- function Get_Tmp_Directory (Host : String) return File_Access is Server : Server_Access; begin if not Is_Configured (Host) then raise Remote_Config_Error with "Invalid FS for host " & Host; else Server := Get_Server (Host); end if; case Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return Create (Host, GNATCOLL.IO.Remote.Unix.Tmp_Dir (Server), False); when FS_Windows => return Create (Host, GNATCOLL.IO.Remote.Windows.Tmp_Dir (Server), False); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & Host; end case; end Get_Tmp_Directory; -------------------- -- Locate_On_Path -- -------------------- function Locate_On_Path (Host : String; Base : FS_String) return File_Access is Server : Server_Access; begin if not Is_Configured (Host) then raise Remote_Config_Error with "Invalid FS for host " & Host; else Server := Get_Server (Host); end if; if GNATCOLL.Path.Is_Absolute_Path (Server.Shell_FS, Base) then return Create (Host, Base, False); end if; case Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => declare Ret : constant FS_String := GNATCOLL.IO.Remote.Unix.Locate_On_Path (Server, Base); begin if Ret = "" then return null; else return Create (Host, Ret, False); end if; end; when FS_Windows => declare Ret : constant FS_String := GNATCOLL.IO.Remote.Windows.Locate_On_Path (Server, Base); begin if Ret = "" then return null; else return Create (Host, Ret, False); end if; end; when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & Host; end case; end Locate_On_Path; ------------------------ -- Get_Logical_Drives -- ------------------------ function Get_Logical_Drives (Host : String) return File_Array is Server : Server_Access; List : GNAT.Strings.String_List_Access; begin if not Is_Configured (Host) then raise Remote_Config_Error with "Invalid FS for host " & Host; else Server := Get_Server (Host); end if; case Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => List := GNATCOLL.IO.Remote.Unix.Get_Logical_Drives (Server); when FS_Windows => List := GNATCOLL.IO.Remote.Windows.Get_Logical_Drives (Server); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & Host; end case; if List = null then return (1 .. 0 => <>); end if; declare Ret : File_Array (1 .. List'Length); begin for J in Ret'Range loop Ret (J) := Create (Host, FS_String (List (List'First + J - Ret'First).all), False); end loop; GNAT.Strings.Free (List); return Ret; end; end Get_Logical_Drives; -------------- -- Get_Host -- -------------- function Get_Host (File : not null access Remote_File_Record) return String is begin if File.Server = null then if not Is_Configured (File.Tmp_Host.all) then return File.Tmp_Host.all; else Internal_Initialize (File, File.Tmp_Host.all, File.Tmp_Path.all); Free (File.Tmp_Host); Free (File.Tmp_Path); return File.Server.Nickname; end if; else return File.Server.Nickname; end if; end Get_Host; ------------------------ -- Dispatching_Create -- ------------------------ overriding function Dispatching_Create (Ref : not null access Remote_File_Record; Full_Path : FS_String) return File_Access is begin return Create (Ref.Get_Host, Full_Path, False); end Dispatching_Create; ------------- -- To_UTF8 -- ------------- overriding function To_UTF8 (Ref : not null access Remote_File_Record; Path : FS_String) return String is pragma Unreferenced (Ref); begin return Codec.To_UTF8 (Path); end To_UTF8; --------------- -- From_UTF8 -- --------------- overriding function From_UTF8 (Ref : not null access Remote_File_Record; Path : String) return FS_String is pragma Unreferenced (Ref); begin return Codec.From_UTF8 (Path); end From_UTF8; -------------- -- Is_Local -- -------------- overriding function Is_Local (File : Remote_File_Record) return Boolean is pragma Unreferenced (File); begin return False; end Is_Local; ------------ -- Get_FS -- ------------ overriding function Get_FS (File : not null access Remote_File_Record) return FS_Type is begin Ensure_Initialized (File); return File.Server.Shell_FS; end Get_FS; ---------------------- -- Resolve_Symlinks -- ---------------------- overriding procedure Resolve_Symlinks (File : not null access Remote_File_Record) is begin Ensure_Initialized (File); -- ??? Should we do something more here (e.g. try to actually resolve ?) if File.Normalized_And_Resolved = null then if File.Normalized = null then File.Normalized := new FS_String' (GNATCOLL.Path.Normalize (Get_FS (File), File.Full.all)); end if; File.Normalized_And_Resolved := File.Normalized; end if; end Resolve_Symlinks; --------------------- -- Is_Regular_File -- --------------------- overriding function Is_Regular_File (File : not null access Remote_File_Record) return Boolean is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Is_Regular_File (File.Server, File.Full.all); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Is_Regular_File (File.Server, File.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Is_Regular_File; ---------- -- Size -- ---------- overriding function Size (File : not null access Remote_File_Record) return Long_Integer is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Size (File.Server, File.Full.all); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Size (File.Server, File.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Size; ------------------ -- Is_Directory -- ------------------ overriding function Is_Directory (File : not null access Remote_File_Record) return Boolean is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Is_Directory (File.Server, File.Full.all); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Is_Directory (File.Server, File.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Is_Directory; ---------------------- -- Is_Symbolic_Link -- ---------------------- overriding function Is_Symbolic_Link (File : not null access Remote_File_Record) return Boolean is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Is_Symbolic_Link (File.Server, File.Full.all); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Is_Symbolic_Link (File.Server, File.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Is_Symbolic_Link; --------------------- -- File_Time_Stamp -- --------------------- overriding function File_Time_Stamp (File : not null access Remote_File_Record) return Ada.Calendar.Time is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.File_Time_Stamp (File.Server, File.Full.all); when FS_Windows => return GNATCOLL.IO.Remote.Windows.File_Time_Stamp (File.Server, File.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end File_Time_Stamp; ----------------- -- Is_Readable -- ----------------- overriding function Is_Readable (File : not null access Remote_File_Record) return Boolean is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Is_Readable (File.Server, File.Full.all); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Is_Readable (File.Server, File.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Is_Readable; ----------------- -- Is_Writable -- ----------------- overriding function Is_Writable (File : not null access Remote_File_Record) return Boolean is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Is_Writable (File.Server, File.Full.all); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Is_Writable (File.Server, File.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Is_Writable; ------------------ -- Set_Writable -- ------------------ overriding procedure Set_Writable (File : not null access Remote_File_Record; State : Boolean) is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => GNATCOLL.IO.Remote.Unix.Set_Writable (File.Server, File.Full.all, State); when FS_Windows => GNATCOLL.IO.Remote.Windows.Set_Writable (File.Server, File.Full.all, State); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Set_Writable; ------------------ -- Set_Readable -- ------------------ overriding procedure Set_Readable (File : not null access Remote_File_Record; State : Boolean) is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => GNATCOLL.IO.Remote.Unix.Set_Readable (File.Server, File.Full.all, State); when FS_Windows => GNATCOLL.IO.Remote.Windows.Set_Readable (File.Server, File.Full.all, State); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Set_Readable; ------------ -- Rename -- ------------ overriding procedure Rename (From : not null access Remote_File_Record; Dest : not null access Remote_File_Record; Success : out Boolean) is begin Ensure_Initialized (From); Ensure_Initialized (Dest); if From.Get_Host /= Dest.Get_Host then raise Remote_Config_Error with "cannot rename a file to another host"; end if; case From.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => GNATCOLL.IO.Remote.Unix.Rename (From.Server, From.Full.all, Dest.Full.all, Success); when FS_Windows => GNATCOLL.IO.Remote.Windows.Rename (From.Server, From.Full.all, Dest.Full.all, Success); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & From.Get_Host; end case; end Rename; ---------- -- Copy -- ---------- overriding procedure Copy (From : not null access Remote_File_Record; Dest : FS_String; Success : out Boolean) is begin Ensure_Initialized (From); case From.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => GNATCOLL.IO.Remote.Unix.Copy (From.Server, From.Full.all, Dest, Success); when FS_Windows => GNATCOLL.IO.Remote.Windows.Copy (From.Server, From.Full.all, Dest, Success); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & From.Get_Host; end case; end Copy; ------------ -- Delete -- ------------ overriding procedure Delete (File : not null access Remote_File_Record; Success : out Boolean) is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => GNATCOLL.IO.Remote.Unix.Delete (File.Server, File.Full.all, Success); when FS_Windows => GNATCOLL.IO.Remote.Windows.Delete (File.Server, File.Full.all, Success); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Delete; --------------------- -- Read_Whole_File -- --------------------- overriding function Read_Whole_File (File : not null access Remote_File_Record) return GNAT.Strings.String_Access is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Read_Whole_File (File.Server, File.Full.all); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Read_Whole_File (File.Server, File.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Read_Whole_File; --------------------- -- Read_Whole_File -- --------------------- overriding function Read_Whole_File (File : not null access Remote_File_Record) return GNATCOLL.Strings.XString is begin Ensure_Initialized (File); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Read_Whole_File (File.Server, File.Full.all); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Read_Whole_File (File.Server, File.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; end Read_Whole_File; ---------------- -- Open_Write -- ---------------- overriding procedure Open_Write (File : not null access Remote_File_Record; Append : Boolean := False; FD : out GNAT.OS_Lib.File_Descriptor; Error : out Ada.Strings.Unbounded.Unbounded_String) is pragma Unreferenced (Error); -- Error diagnostics is not implemented for remote files. Tmp_Dir : File_Access := GNATCOLL.IO.Native.Get_Tmp_Directory; Cur_Dir : File_Access := GNATCOLL.IO.Native.Current_Dir; Tmp : constant FS_String := GNATCOLL.Path.Ensure_Directory (Tmp_Dir.Get_FS, Tmp_Dir.Full.all); Cur : constant FS_String := GNATCOLL.Path.Ensure_Directory (Cur_Dir.Get_FS, Cur_Dir.Full.all); Content : GNAT.Strings.String_Access; Written : Integer; pragma Unreferenced (Written); begin Ensure_Initialized (File); Unref (Tmp_Dir); Unref (Cur_Dir); GNAT.Directory_Operations.Change_Dir (String (Tmp)); GNAT.OS_Lib.Create_Temp_File (FD, File.Tmp_Name); GNAT.Directory_Operations.Change_Dir (String (Cur)); if Append then case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => Content := GNATCOLL.IO.Remote.Unix.Read_Whole_File (File.Server, File.Full.all); when FS_Windows => Content := GNATCOLL.IO.Remote.Windows.Read_Whole_File (File.Server, File.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; Written := GNAT.OS_Lib.Write (FD, Content.all'Address, Content'Length); GNAT.Strings.Free (Content); end if; end Open_Write; ----------- -- Close -- ----------- overriding procedure Close (File : not null access Remote_File_Record; FD : GNAT.OS_Lib.File_Descriptor; Success : out Boolean) is Content : GNAT.Strings.String_Access; Tmp_Dir : File_Access := GNATCOLL.IO.Native.Get_Tmp_Directory; Tmp : constant FS_String := GNATCOLL.Path.Ensure_Directory (Tmp_Dir.Get_FS, Tmp_Dir.Full.all); Dead : Boolean; pragma Unreferenced (Dead); begin Unref (Tmp_Dir); Ensure_Initialized (File); GNAT.OS_Lib.Close (FD); Content := GNATCOLL.Mmap.Read_Whole_File (String (Tmp) & File.Tmp_Name, Empty_If_Not_Found => True); case File.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => Success := GNATCOLL.IO.Remote.Unix.Write_File (File.Server, File.Full.all, Content.all); when FS_Windows => Success := GNATCOLL.IO.Remote.Windows.Write_File (File.Server, File.Full.all, Content.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & File.Get_Host; end case; GNAT.OS_Lib.Delete_File (String (Tmp) & File.Tmp_Name, Dead); end Close; ---------------- -- Change_Dir -- ---------------- overriding function Change_Dir (Dir : not null access Remote_File_Record) return Boolean is begin Ensure_Initialized (Dir); case Dir.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Change_Dir (Dir.Server, Dir.Full.all); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Change_Dir (Dir.Server, Dir.Full.all); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & Dir.Get_Host; end case; end Change_Dir; -------------- -- Read_Dir -- -------------- overriding function Read_Dir (Dir : not null access Remote_File_Record; Dirs_Only : Boolean := False; Files_Only : Boolean := False) return GNAT.Strings.String_List is begin Ensure_Initialized (Dir); case Dir.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Read_Dir (Dir.Server, Dir.Full.all, Dirs_Only, Files_Only); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Read_Dir (Dir.Server, Dir.Full.all, Dirs_Only, Files_Only); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & Dir.Get_Host; end case; end Read_Dir; -------------- -- Make_Dir -- -------------- overriding function Make_Dir (Dir : not null access Remote_File_Record; Recursive : Boolean) return Boolean is begin Ensure_Initialized (Dir); case Dir.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => return GNATCOLL.IO.Remote.Unix.Make_Dir (Dir.Server, Dir.Full.all, Recursive); when FS_Windows => return GNATCOLL.IO.Remote.Windows.Make_Dir (Dir.Server, Dir.Full.all, Recursive); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & Dir.Get_Host; end case; end Make_Dir; ---------------- -- Remove_Dir -- ---------------- overriding procedure Remove_Dir (Dir : not null access Remote_File_Record; Recursive : Boolean; Success : out Boolean) is begin Ensure_Initialized (Dir); case Dir.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => GNATCOLL.IO.Remote.Unix.Delete_Dir (Dir.Server, Dir.Full.all, Recursive, Success); when FS_Windows => GNATCOLL.IO.Remote.Windows.Delete_Dir (Dir.Server, Dir.Full.all, Recursive, Success); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & Dir.Get_Host; end case; end Remove_Dir; -------------- -- Copy_Dir -- -------------- overriding procedure Copy_Dir (From : not null access Remote_File_Record; Dest : FS_String; Success : out Boolean) is begin Ensure_Initialized (From); case From.Server.Shell_FS is when FS_Unix | FS_Unix_Case_Insensitive => GNATCOLL.IO.Remote.Unix.Copy_Dir (From.Server, From.Full.all, Dest, Success); when FS_Windows => GNATCOLL.IO.Remote.Windows.Copy_Dir (From.Server, From.Full.all, Dest, Success); when FS_Unknown => raise Remote_Config_Error with "Invalid FS for host " & From.Get_Host; end case; end Copy_Dir; --------------------------- -- Copy_File_Permissions -- --------------------------- overriding procedure Copy_File_Permissions (From, To : not null access Remote_File_Record; Success : out Boolean) is pragma Unreferenced (From, To); begin Success := False; end Copy_File_Permissions; ----------- -- Codec -- ----------- package body Codec is ------------- -- To_UTF8 -- ------------- function To_UTF8 (Path : FS_String) return String is begin -- ??? What if the Transport uses a specific charset ? return String (Path); end To_UTF8; --------------- -- From_UTF8 -- --------------- function From_UTF8 (Path : String) return FS_String is begin -- ??? What if the Transport uses a specific charset ? return FS_String (Path); end From_UTF8; end Codec; end GNATCOLL.IO.Remote; gnatcoll-core-21.0.0/src/gnatcoll-opt_parse.adb0000644000175000017500000010373413743647711021246 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Exceptions; with Ada.Unchecked_Deallocation; with GNAT.OS_Lib; with GNATCOLL.VFS; package body GNATCOLL.Opt_Parse is package Cmd_Line renames Ada.Command_Line; function "+" (Self : String) return XString renames To_XString; function "+" (Self : XString) return String renames To_String; function Get_Arguments (Arguments : XString_Array) return XString_Array; function Parse_One_Option (Short, Long : String; Args : XString_Array; Pos : Positive; New_Pos : out Parser_Return) return XString; ------------------ -- Text wrapper -- ------------------ type Text_Wrapper is tagged record Lines : XString_Vectors.Vector; Wrap_Col : Natural := 80; Start_Col : Natural := 0; end record; -- Simple abstract type to help with formatting of the outputted help text. subtype Col_Type is Integer range -2 .. Integer'Last; -- Type for a column in the text wrapper. Current_Col : constant Col_Type := -1; -- Constant to represent a magic value that represents the current column No_Col : constant Col_Type := -2; -- Constant used to represent the absence of column pragma Warnings (Off, "not dispatching"); procedure Append_Text (Self : in out Text_Wrapper; Text : String; Cut : Boolean := True); -- Append some text to Self. If `Cut` is True, the text will be cut and -- wrapped on every white space character encountered. procedure Set_Next_Start_Column (Self : in out Text_Wrapper; Col : Col_Type := 0); -- Trigger the text wrapper so that next time a line implicitly (through -- wrapping) or explicitly appended, it will begin at column `Col`. procedure Set_Column (Self : in out Text_Wrapper; Col : Col_Type); -- This operation will set the column to write on for the *current line* -- to Col, and set the next start column to `Col`, so that text on -- subsequent lines starts at `Col`. subtype XString_Ref is XString_Vectors.Reference_Type; -- Shortcut for a reference to a XString function Append_Line (Self : aliased in out Text_Wrapper) return XString_Ref; -- Append a new line to Self procedure Append_Line (Self : in out Text_Wrapper; Text : String := ""; Col_After : Col_Type := No_Col); -- Append a new line to Self, after appending `Text`. If -- `Col_After` is not `No_Col`, then set the next start column to -- `Col_After`. function Current_Line (Self : aliased in out Text_Wrapper) return XString_Ref is (if Self.Lines.Is_Empty then Self.Append_Line else Self.Lines.Reference (Self.Lines.Last_Index)); -- Return a reference to the current line. function Render (Self : Text_Wrapper) return String; -- Render the content of Self to a String. pragma Warnings (On, "not dispatching"); ----------------- -- Flag_Parser -- ----------------- type Flag_Parser is new Parser_Type with record Short, Long : XString; end record; type Flag_Parser_Result is new Parser_Result with record Result : Boolean; end record; overriding function Usage (Self : Flag_Parser) return String is ("[" & To_String (Self.Long) & (if Self.Short = "" then "" else "|" & To_String (Self.Short)) & "]"); overriding function Help_Name (Self : Flag_Parser) return String is (To_String (Self.Long) & ", " & To_String (Self.Short)); overriding function Parse_Args (Self : in out Flag_Parser; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return; overriding procedure Release (Self : in out Flag_Parser_Result) is null; type Help_Flag_Parser is new Flag_Parser with null record; -- Specific subtype of Flag_Parser to designate the help flag parser. ----------------- -- Append_Line -- ----------------- procedure Append_Line (Self : in out Text_Wrapper; Text : String := ""; Col_After : Col_Type := No_Col) is begin if Text /= "" then Self.Append_Text (Text); end if; if Col_After /= No_Col then Self.Set_Next_Start_Column (Col_After); end if; declare Dummy : XString_Ref := Self.Append_Line; begin null; end; end Append_Line; ---------------- -- Set_Column -- ---------------- procedure Set_Column (Self : in out Text_Wrapper; Col : Col_Type) is begin Self.Set_Next_Start_Column (Col); if Self.Current_Line.Length > Col then Self.Append_Line; else Self.Append_Text ((1 .. Col - Self.Current_Line.Length => ' '), False); end if; end Set_Column; ------------ -- Render -- ------------ function Render (Self : Text_Wrapper) return String is Res : XString; begin for El of Self.Lines loop Res.Append (El); Res.Append (ASCII.LF); end loop; return +Res; end Render; ----------------- -- Append_Line -- ----------------- function Append_Line (Self : aliased in out Text_Wrapper) return XString_Vectors.Reference_Type is Ret : XString; begin Self.Lines.Append (Ret); declare L : constant XString_Ref := Self.Current_Line; begin if Self.Start_Col > 0 then L.Append ((1 .. Self.Start_Col => ' ')); end if; return L; end; end Append_Line; ----------------- -- Append_Text -- ----------------- procedure Append_Text (Self : in out Text_Wrapper; Text : String; Cut : Boolean := True) is begin if Cut then declare Cut_Text : constant XString_Array := XString'(+Text).Split (' '); begin for I in Cut_Text'Range loop if I > 1 then Append_Text (Self, " ", False); end if; Append_Text (Self, +(Cut_Text (I)), False); end loop; end; else declare Cur_Line : constant XString_Ref := Self.Current_Line; begin if Cur_Line.Length + Text'Length <= Self.Wrap_Col then Cur_Line.Append (Text); return; end if; end; declare Cur_Line : constant XString_Ref := Append_Line (Self); begin Cur_Line.Append (Text); end; end if; end Append_Text; --------------------------- -- Set_Next_Start_Column -- --------------------------- procedure Set_Next_Start_Column (Self : in out Text_Wrapper; Col : Col_Type := 0) is begin if Col = Current_Col then Self.Start_Col := Col_Type (Self.Current_Line.Length); else Self.Start_Col := Col; end if; end Set_Next_Start_Column; ------------------- -- Get_Arguments -- ------------------- function Get_Arguments (Arguments : XString_Array) return XString_Array is begin if Arguments /= No_Arguments then return Arguments; end if; declare Args : XString_Array (1 .. Cmd_Line.Argument_Count); begin for I in Args'Range loop Args (I) := +Cmd_Line.Argument (I); end loop; return Args; end; end Get_Arguments; ---------------- -- Get_Result -- ---------------- function Get_Result (Self : Parser_Type'Class; Args : Parsed_Arguments) return Parser_Result_Access is -- Due to controlled objects finalization, the following code is not -- thread safe, so we use a critical section here, so that Get functions -- for arguments are thread safe. Dummy : Scoped_Lock (Self.Parser.Mutex'Access); begin declare Real_Args : Parsed_Arguments; begin if Args = No_Parsed_Arguments then Real_Args := Self.Parser.Default_Result; else Real_Args := Args; end if; if Real_Args = No_Parsed_Arguments then raise Opt_Parse_Error with "No results for command line arguments"; end if; return Real_Args.Ref.Get.Results (Self.Position); end; end Get_Result; ---------------- -- Has_Result -- ---------------- function Has_Result (Self : Parser_Type'Class; Args : Parsed_Arguments) return Boolean is begin return Self.Get_Result (Args) /= null; end Has_Result; ----------- -- Parse -- ----------- function Parse (Self : in out Argument_Parser; Arguments : XString_Array := No_Arguments) return Boolean is begin if Self.Data.Default_Result /= No_Parsed_Arguments then Self.Data.Default_Result := No_Parsed_Arguments; end if; return Ret : constant Boolean := Self.Parse (Arguments, Self.Data.Default_Result) do if not Ret then Put_Line (Help (Self)); end if; end return; end Parse; ----------- -- Parse -- ----------- function Parse (Self : in out Argument_Parser; Arguments : XString_Array := No_Arguments; Result : out Parsed_Arguments) return Boolean is Current_Arg : Positive := 1; Cmd_Line_Args : constant XString_Array := Get_Arguments (Arguments); procedure Handle_Failure (Error_Msg : String); -------------------- -- Handle_Failure -- -------------------- procedure Handle_Failure (Error_Msg : String) is begin Put_Line ("Argument parsing failed: " & Error_Msg); end Handle_Failure; begin Result.Ref.Set (Parsed_Arguments_Type' (Raw_Args => new XString_Array'(Cmd_Line_Args), Results => new Parser_Result_Array (1 .. Self.Data.All_Parsers.Last_Index))); while Current_Arg <= Cmd_Line_Args'Last loop for Opt_Parser of Self.Data.Opts_Parsers loop begin declare P_Return : constant Parser_Return := Opt_Parser.Parse (Cmd_Line_Args, Current_Arg, Result); begin if P_Return /= Error_Return then Current_Arg := Positive (P_Return); if Opt_Parser.all in Help_Flag_Parser'Class then Put_Line (Self.Help); GNAT.OS_Lib.OS_Exit (0); end if; goto Next_Iter; end if; end; exception when E : Opt_Parse_Error => Handle_Failure ("for option " & (+Opt_Parser.Name) & " - " & Ada.Exceptions.Exception_Message (E)); return False; end; end loop; for Pos_Parser of Self.Data.Positional_Args_Parsers loop begin declare P_Return : constant Parser_Return := Pos_Parser.Parse (Cmd_Line_Args, Current_Arg, Result); begin if P_Return /= Error_Return then Current_Arg := Positive (P_Return); goto Next_Iter; end if; end; exception when E : Opt_Parse_Error => Handle_Failure ("for parser " & (+Pos_Parser.Name) & " - " & Ada.Exceptions.Exception_Message (E)); return False; end; end loop; Handle_Failure ("Unrecognized argument " & (+Cmd_Line_Args (Current_Arg))); return False; <> end loop; for Parser of Self.Data.All_Parsers loop if not Parser.Opt and then not Parser.Has_Result (Result) then Handle_Failure ("Missing value for " & (+Parser.Name)); return False; end if; end loop; return True; exception when E : Opt_Parse_Error => Handle_Failure (Ada.Exceptions.Exception_Message (E)); return False; end Parse; ----------- -- Parse -- ----------- function Parse (Self : in out Parser_Type'Class; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return is begin if Self.Has_Result (Result) and then not Self.Does_Accumulate then return Error_Return; end if; return Self.Parse_Args (Args, Pos, Result); end Parse; ------------------------------- -- Parse_Positional_Arg_List -- ------------------------------- package body Parse_Positional_Arg_List is type Result_Array_Access is access all Result_Array; type Positional_Arg_List_Parser is new Parser_Type with record null; end record; overriding function Parse_Args (Self : in out Positional_Arg_List_Parser; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return; overriding function Usage (Self : Positional_Arg_List_Parser) return String is (Name & " [" & Name & " ...]"); type Internal_Result is new Parser_Result with record Results : Result_Array_Access; end record; type Internal_Result_Access is access all Internal_Result; overriding procedure Release (Self : in out Internal_Result); Self_Val : aliased Positional_Arg_List_Parser := Positional_Arg_List_Parser' (Name => +Name, Help => +Help, Parser => Parser.Data, Position => <>, Opt => Allow_Empty); Self : constant Parser_Access := Self_Val'Unchecked_Access; --------- -- Get -- --------- function Get (Args : Parsed_Arguments := No_Parsed_Arguments) return Result_Array is (if Enabled then (if Self.Has_Result (Args) then Internal_Result (Self.Get_Result (Args).all).Results.all else No_Results) elsif Allow_Empty then (1 .. 0 => <>) else raise Disabled_Error); ---------------- -- Parse_Args -- ---------------- overriding function Parse_Args (Self : in out Positional_Arg_List_Parser; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return is Last : Parser_Return := Error_Return; begin for I in Pos .. Args'Last loop if Args (I).Starts_With ("--") or Args (I).Starts_With ("-") then exit; end if; Last := I; end loop; if Last = Error_Return then return Error_Return; end if; declare R : Result_Array (1 .. Last - Pos + 1); begin for I in R'Range loop R (I) := Convert (+Args (I + Pos - 1)); end loop; declare Res : constant Internal_Result_Access := new Internal_Result' (Start_Pos => Pos, End_Pos => Last, Results => new Result_Array'(R)); begin Result.Ref.Get.Results (Self.Position) := Res.all'Unchecked_Access; end; end; return Parser_Return (Last + 1); end Parse_Args; overriding procedure Release (Self : in out Internal_Result) is procedure Free is new Ada.Unchecked_Deallocation (Result_Array, Result_Array_Access); begin Free (Self.Results); end Release; begin if Enabled then Parser.Data.Positional_Args_Parsers.Append (Self); Parser.Data.All_Parsers.Append (Self); Self.Position := Parser.Data.All_Parsers.Last_Index; end if; end Parse_Positional_Arg_List; -------------------------- -- Parse_Positional_Arg -- -------------------------- package body Parse_Positional_Arg is type Positional_Arg_Parser is new Parser_Type with record null; end record; overriding function Parse_Args (Self : in out Positional_Arg_Parser; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return; overriding function Usage (Self : Positional_Arg_Parser) return String is (Name); type Internal_Result is new Parser_Result with record Result : Arg_Type; end record; type Internal_Result_Access is access all Internal_Result; overriding procedure Release (Self : in out Internal_Result) is null; Self_Val : aliased Positional_Arg_Parser := (Name => +Name, Help => +Help, Parser => Parser.Data, Position => <>, Opt => False); Self : constant Parser_Access := Self_Val'Unchecked_Access; --------- -- Get -- --------- function Get (Args : Parsed_Arguments := No_Parsed_Arguments) return Arg_Type is (if Enabled then Internal_Result (Self.Get_Result (Args).all).Result else raise Disabled_Error); ---------------- -- Parse_Args -- ---------------- overriding function Parse_Args (Self : in out Positional_Arg_Parser; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return is begin if Args (Pos).Starts_With ("--") or Args (Pos).Starts_With ("-") then return Error_Return; end if; declare Res : constant Arg_Type := Convert (+Args (Pos)); Int_Res : constant Internal_Result_Access := new Internal_Result' (Start_Pos => Pos, End_Pos => Pos, Result => Res); begin Result.Ref.Get.Results (Self.Position) := Int_Res.all'Unchecked_Access; end; return Parser_Return (Pos + 1); end Parse_Args; begin if Enabled then Parser.Data.Positional_Args_Parsers.Append (Self); Parser.Data.All_Parsers.Append (Self); Self.Position := Parser.Data.All_Parsers.Last_Index; end if; end Parse_Positional_Arg; ---------------- -- Parse_Args -- ---------------- overriding function Parse_Args (Self : in out Flag_Parser; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return is begin if Args (Pos) = Self.Long or else Args (Pos) = Self.Short then declare Res : constant Parser_Result_Access := new Flag_Parser_Result' (Start_Pos => Pos, End_Pos => Pos, Result => True); begin Result.Ref.Get.Results (Self.Position) := Res; end; return Parser_Return (Pos + 1); else return Error_Return; end if; end Parse_Args; ---------------- -- Parse_Flag -- ---------------- package body Parse_Flag is Self_Val : aliased Flag_Parser := Flag_Parser' (Name => +Long (3 .. Long'Last), Help => +Help, Long => +Long, Short => +Short, Parser => Parser.Data, Opt => True, Position => <>); Self : constant Parser_Access := Self_Val'Unchecked_Access; --------- -- Get -- --------- function Get (Args : Parsed_Arguments := No_Parsed_Arguments) return Boolean is begin if not Enabled then return False; end if; declare R : constant Parser_Result_Access := Self.Get_Result (Args); begin if R /= null then return Flag_Parser_Result (R.all).Result; else return False; end if; end; end Get; begin if Enabled then Parser.Data.Opts_Parsers.Append (Self); Parser.Data.All_Parsers.Append (Self); Self.Position := Parser.Data.All_Parsers.Last_Index; end if; end Parse_Flag; ------------------ -- Parse_Option -- ------------------ package body Parse_Option is type Option_Parser is new Parser_Type with record null; end record; overriding function Usage (Self : Option_Parser) return String is ("[" & Long & (if Short = "" then "" else "|" & Short) & " " & To_Upper (Long (3 .. Long'Last)) & "]"); overriding function Help_Name (Dummy : Option_Parser) return String is (Long & ", " & Short); overriding function Parse_Args (Self : in out Option_Parser; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return; type Internal_Result is new Parser_Result with record Result : Arg_Type; end record; type Internal_Result_Access is access all Internal_Result; procedure Release (Self : in out Internal_Result) is null; Self_Val : aliased Option_Parser := Option_Parser' (Name => +Long (3 .. Long'Last), Help => +Help, Parser => Parser.Data, Opt => True, Position => <>); Self : constant Parser_Access := Self_Val'Unchecked_Access; --------- -- Get -- --------- function Get (Args : Parsed_Arguments := No_Parsed_Arguments) return Arg_Type is begin if not Enabled then return Default_Val; end if; declare R : constant Parser_Result_Access := Self.Get_Result (Args); begin if R /= null then return Internal_Result (R.all).Result; else return Default_Val; end if; end; end Get; ---------------- -- Parse_Args -- ---------------- overriding function Parse_Args (Self : in out Option_Parser; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return is New_Pos : Parser_Return; Raw : constant XString := Parse_One_Option (Short, Long, Args, Pos, New_Pos); begin if New_Pos /= Error_Return then declare Res : constant Internal_Result_Access := new Internal_Result'(Start_Pos => Pos, End_Pos => Pos, Result => Convert (+Raw)); begin Result.Ref.Get.Results (Self.Position) := Res.all'Unchecked_Access; end; end if; return New_Pos; end Parse_Args; begin if Enabled then Parser.Data.Opts_Parsers.Append (Self); Parser.Data.All_Parsers.Append (Self); Self.Position := Parser.Data.All_Parsers.Last_Index; end if; end Parse_Option; ----------------------- -- Parse_Option_List -- ----------------------- package body Parse_Option_List is package Result_Vectors is new Ada.Containers.Vectors (Positive, Arg_Type); type Option_List_Parser is new Parser_Type with record null; end record; overriding function Usage (Self : Option_List_Parser) return String is ("[" & Long & (if Short = "" then "" else "|" & Short) & " " & To_Upper (Long (3 .. Long'Last)) & "[" & To_Upper (Long (3 .. Long'Last)) & "...]]"); overriding function Help_Name (Dummy : Option_List_Parser) return String is (Long & ", " & Short); overriding function Parse_Args (Self : in out Option_List_Parser; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return; overriding function Does_Accumulate (Self : Option_List_Parser) return Boolean is (Accumulate); type Internal_Result is new Parser_Result with record Results : Result_Vectors.Vector; end record; type Internal_Result_Access is access all Internal_Result; procedure Release (Self : in out Internal_Result) is null; Self_Val : aliased Option_List_Parser := (Name => +Long (3 .. Long'Last), Help => +Help, Parser => Parser.Data, Opt => True, Position => <>); Self : constant Parser_Access := Self_Val'Unchecked_Access; --------- -- Get -- --------- function Get (Args : Parsed_Arguments := No_Parsed_Arguments) return Result_Array is begin if not Enabled then return (1 .. 0 => <>); end if; declare R : constant Parser_Result_Access := Self.Get_Result (Args); begin if R /= null then declare Res : Result_Array (1 .. Internal_Result (R.all).Results.Last_Index); begin for I in Res'Range loop Res (I) := Internal_Result (R.all).Results (I); end loop; return Res; end; else return No_Results; end if; end; end Get; ---------------- -- Parse_Args -- ---------------- overriding function Parse_Args (Self : in out Option_List_Parser; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return is Last : Parser_Return := Error_Return; Res : Parser_Result_Access renames Result.Ref.Get.Results (Self.Position); Tmp : Internal_Result_Access := null; begin if Accumulate then declare New_Pos : Parser_Return; Raw : constant XString := Parse_One_Option (Short, Long, Args, Pos, New_Pos); begin if New_Pos /= Error_Return then if Res = null then Tmp := new Internal_Result' (Start_Pos => Pos, End_Pos => Pos, Results => Result_Vectors.Empty_Vector); Res := Tmp.all'Unchecked_Access; end if; Internal_Result (Res.all).Results.Append (Convert (+Raw)); end if; return New_Pos; end; end if; if Args (Pos) /= +Long and then Args (Pos) /= +Short then return Error_Return; end if; for I in Pos + 1 .. Args'Last loop if Args (I).Starts_With ("--") or Args (I).Starts_With ("-") then exit; end if; Last := I; end loop; if Last = Error_Return then return Error_Return; end if; Tmp := new Internal_Result' (Start_Pos => Pos, End_Pos => Last, Results => Result_Vectors.Empty_Vector); Res := Tmp.all'Unchecked_Access; for I in 1 .. Last - Pos + 1 loop Internal_Result (Res.all).Results (I) := Convert (+Args (I + Pos)); end loop; return Parser_Return (Last + 1); end Parse_Args; begin if Enabled then Parser.Data.Opts_Parsers.Append (Self); Parser.Data.All_Parsers.Append (Self); Self.Position := Parser.Data.All_Parsers.Last_Index; end if; end Parse_Option_List; ---------------------------- -- Create_Argument_Parser -- ---------------------------- function Create_Argument_Parser (Help : String; Command_Name : String := "") return Argument_Parser is XCommand_Name : constant XString := +(if Command_Name = "" then String (VFS.Base_Name (VFS.Create (VFS.Filesystem_String (Ada.Command_Line.Command_Name)))) else Command_Name); begin return Parser : Argument_Parser do Parser.Data := new Argument_Parser_Data'(+Help, XCommand_Name, others => <>); Parser.Data.Help_Flag := new Help_Flag_Parser' (Name => +"help", Help => +"Show this help message", Position => <>, Opt => True, Parser => Parser.Data, Short => +"-h", Long => +"--help"); Parser.Data.Opts_Parsers.Append (Parser.Data.Help_Flag); Parser.Data.All_Parsers.Append (Parser.Data.Help_Flag); Parser.Data.Help_Flag.Position := Parser.Data.All_Parsers.Last_Index; end return; end Create_Argument_Parser; ---------- -- Help -- ---------- function Help (Self : Argument_Parser) return String is Ret : Text_Wrapper; begin -- Usage Ret.Append_Text ("usage: " & (+Self.Data.Command_Name)); Ret.Set_Next_Start_Column (Current_Col); Ret.Append_Text (" "); for Parser of Self.Data.All_Parsers loop Ret.Append_Text (Parser.Usage); Ret.Append_Text (" "); end loop; Ret.Append_Line (Col_After => 0); Ret.Append_Line; -- Main help Ret.Append_Text (+Self.Data.Help); Ret.Append_Line; Ret.Append_Line; Ret.Append_Line ("positional arguments:", Col_After => 3); for Parser of Self.Data.Positional_Args_Parsers loop Ret.Append_Text (Parser.Help_Name); Ret.Set_Column (25); Ret.Append_Line (+Parser.Help, Col_After => 3); end loop; Ret.Append_Line (Col_After => 0); Ret.Append_Line ("optional arguments:", Col_After => 3); for Parser of Self.Data.Opts_Parsers loop Ret.Append_Text (Parser.Help_Name); Ret.Set_Column (25); Ret.Append_Line (+Parser.Help, Col_After => 3); end loop; return Ret.Render; end Help; ------------- -- Convert -- ------------- function Convert (Arg : String) return Integer is begin return Integer'Value (Arg); exception when Constraint_Error => raise Opt_Parse_Error with "wrong value for Integer: """ & Arg & """"; end Convert; ---------------------- -- Parse_One_Option -- ---------------------- function Parse_One_Option (Short, Long : String; Args : XString_Array; Pos : Positive; New_Pos : out Parser_Return) return XString is begin if Args (Pos) = Long or else (Short /= "" and then Args (Pos) = Short) then if Pos + 1 > Args'Last then raise Opt_Parse_Error with "Incomplete option"; end if; New_Pos := Pos + 2; return Args (Pos + 1); elsif Args (Pos).Starts_With (Long & "=") then New_Pos := Pos + 1; return Args (Pos).Slice (Long'Last + 2, Args (Pos).Length); elsif Short /= "" and then Args (Pos).Starts_With (Short) then New_Pos := Pos + 1; return Args (Pos).Slice (Short'Last + 1, Args (Pos).Length); else New_Pos := Error_Return; return +""; end if; end Parse_One_Option; ------------- -- Release -- ------------- procedure Release (Self : in out Parsed_Arguments_Type) is procedure Free is new Ada.Unchecked_Deallocation (Parser_Result'Class, Parser_Result_Access); procedure Free is new Ada.Unchecked_Deallocation (Parser_Result_Array, Parser_Result_Array_Access); procedure Free is new Ada.Unchecked_Deallocation (XString_Array, XString_Array_Access); begin Free (Self.Raw_Args); for R of Self.Results.all loop if R /= null then R.Release; end if; Free (R); end loop; Free (Self.Results); end Release; -------------- -- Finalize -- -------------- overriding procedure Finalize (Self : in out Argument_Parser) is procedure Free is new Ada.Unchecked_Deallocation (Argument_Parser_Data, Argument_Parser_Data_Access); procedure Free is new Ada.Unchecked_Deallocation (Parser_Type'Class, Parser_Access); begin Free (Self.Data.Help_Flag); Free (Self.Data); end Finalize; end GNATCOLL.Opt_Parse; gnatcoll-core-21.0.0/src/gnatcoll-io-remote.ads0000644000175000017500000001606213661715457021172 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Remote; package GNATCOLL.IO.Remote is Remote_Config_Error : exception; type Remote_File_Record is new File_Record with private; type Remote_File_Access is access all Remote_File_Record'Class; function Create (Host : String; Path : FS_String; Normalize : Boolean) return File_Access; function Current_Dir (Host : String) return File_Access; function Home_Dir (Host : String) return File_Access; function Get_Tmp_Directory (Host : String) return File_Access; function Get_Logical_Drives (Host : String) return File_Array; function Locate_On_Path (Host : String; Base : FS_String) return File_Access; procedure Ensure_Initialized (File : not null access Remote_File_Record'Class); -- Same as above, raising an exception if the file cannot be initialized. function Get_Host (File : not null access Remote_File_Record) return String; ---------------------------- -- Overridden from parent -- ---------------------------- overriding function Dispatching_Create (Ref : not null access Remote_File_Record; Full_Path : FS_String) return File_Access; overriding function To_UTF8 (Ref : not null access Remote_File_Record; Path : FS_String) return String; overriding function From_UTF8 (Ref : not null access Remote_File_Record; Path : String) return FS_String; overriding function Is_Local (File : Remote_File_Record) return Boolean; overriding function Get_FS (File : not null access Remote_File_Record) return FS_Type; overriding procedure Resolve_Symlinks (File : not null access Remote_File_Record); overriding function Is_Regular_File (File : not null access Remote_File_Record) return Boolean; overriding function Size (File : not null access Remote_File_Record) return Long_Integer; overriding function Is_Directory (File : not null access Remote_File_Record) return Boolean; overriding function Is_Symbolic_Link (File : not null access Remote_File_Record) return Boolean; overriding function File_Time_Stamp (File : not null access Remote_File_Record) return Ada.Calendar.Time; overriding function Is_Writable (File : not null access Remote_File_Record) return Boolean; overriding procedure Set_Writable (File : not null access Remote_File_Record; State : Boolean); overriding function Is_Readable (File : not null access Remote_File_Record) return Boolean; overriding procedure Set_Readable (File : not null access Remote_File_Record; State : Boolean); overriding procedure Rename (From : not null access Remote_File_Record; Dest : not null access Remote_File_Record; Success : out Boolean); overriding procedure Copy (From : not null access Remote_File_Record; Dest : FS_String; Success : out Boolean); overriding procedure Delete (File : not null access Remote_File_Record; Success : out Boolean); overriding function Read_Whole_File (File : not null access Remote_File_Record) return GNAT.Strings.String_Access; overriding function Read_Whole_File (File : not null access Remote_File_Record) return GNATCOLL.Strings.XString; overriding procedure Open_Write (File : not null access Remote_File_Record; Append : Boolean := False; FD : out GNAT.OS_Lib.File_Descriptor; Error : out Ada.Strings.Unbounded.Unbounded_String); overriding procedure Close (File : not null access Remote_File_Record; FD : GNAT.OS_Lib.File_Descriptor; Success : out Boolean); overriding function Change_Dir (Dir : not null access Remote_File_Record) return Boolean; overriding function Read_Dir (Dir : not null access Remote_File_Record; Dirs_Only : Boolean := False; Files_Only : Boolean := False) return GNAT.Strings.String_List; overriding function Make_Dir (Dir : not null access Remote_File_Record; Recursive : Boolean) return Boolean; overriding procedure Remove_Dir (Dir : not null access Remote_File_Record; Recursive : Boolean; Success : out Boolean); overriding procedure Copy_Dir (From : not null access Remote_File_Record; Dest : FS_String; Success : out Boolean); overriding procedure Copy_File_Permissions (From, To : not null access Remote_File_Record; Success : out Boolean); -- See parent for documentation package Codec is function To_UTF8 (Path : FS_String) return String; function From_UTF8 (Path : String) return FS_String; end Codec; -- Codec to translate a path to/from utf-8. private type Remote_File_Record is new File_Record with record Tmp_Host : GNAT.Strings.String_Access; -- Host. Saved in case the below server is not resolved immediately. Tmp_Path : FS_String_Access; -- Path used at creation, saved in case the below server is not resolved -- immediately. Tmp_Norm : Boolean; -- Value used at creation to determine if Tmp_Path should be normalized Server : GNATCOLL.Remote.Server_Access; -- The server on which the file commands are executed. Tmp_Name : GNAT.OS_Lib.Temp_File_Name; -- Saved name for the temporary file used during Write operations. end record; end GNATCOLL.IO.Remote; gnatcoll-core-21.0.0/src/gnatcoll-geometry.ads0000644000175000017500000001703413661715457021125 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- A set of planar geometric utilities (intersections of segments, etc). with Ada.Numerics.Generic_Elementary_Functions; generic type Coordinate is digits <>; -- The type used to represent coordinates and distances. -- Distances are returned as a subtype including only the positive (or -- null) range, so this type must include positive numbers. package GNATCOLL.Geometry is package Coordinate_Elementary_Functions is new Ada.Numerics.Generic_Elementary_Functions (Coordinate); subtype Distance_Type is Coordinate'Base range 0.0 .. Coordinate'Base'Last; type Point is record X, Y : Coordinate; end record; -- General representation for a point in 2D No_Point : constant Point; -- Constant used to indicate that the point doesn't exist (no intersection -- for instance) Infinity_Points : constant Point; -- Constant used to indicate that an infinity number of points would match, -- for instance in the case of the intersection of coincident lines type Polygon is array (Positive range <>) of Point; type Triangle is new Polygon (1 .. 3); type Segment is array (1 .. 2) of Point; type Vector is new Point; type Rectangle is new Polygon (1 .. 2); -- A rectangle has sides parallel to the two axis of the space coordinates. -- It is defined by its top-left and bottom-right corners. If you need a -- more general definition of a rectangle, use the generic algorithms on -- polygons. type Line is private; type Circle is record Center : Point; Radius : Coordinate; end record; No_Circle : constant Circle; function To_Vector (S : Segment) return Vector; -- Return the vector that indicates the direction and magnitude of the -- segment. function To_Line (P1, P2 : Point) return Line; function To_Line (Seg : Segment) return Line; -- Return the line going through the two points, or overlapping the -- segment. function To_Circle (P1, P2, P3 : Point) return Circle; -- Return the circle that passes through the 3 points. If the 3 points are -- colinear, No_Circle is returned. function "-" (P2, P1 : Point) return Vector; -- Return the vector to go from P1 to P2. function Dot (Vector1, Vector2 : Vector) return Coordinate; -- Return the dot product of Vector1 and Vector2. Mathematically, this is -- also the value of |Vector1| * |Vector2| * cos (alpha), where alpha is -- the angle between the two vectors. When Dot is 0, the two vectors are -- orthogonal or null. function Cross (Vector1, Vector2 : Vector) return Coordinate; -- Return the magnitude of the cross-product of the two vectors. -- Technically, this is also a vector, but since we are in 2D, this is -- represented as a scalar. -- -- In 2D, this is also the value of |Vector1| * |Vector2| * sin (alpha). -- This is positive if Vector1 is less than 180 degrees clockwise from B. -- -- Last, in 2D the cross product is also the area of the parallelogram with -- two of its side formed by Vector1 and Vector2. -- ----A--- -- \ \ -- B | -- \ \ -- --------- function Length (Vect : Vector) return Distance_Type; -- Return the magnitude of the vector function Bisector (S : Segment) return Line; pragma Inline (Bisector); -- Return the bisector to S, i.e. the line that is perpendicular to S and -- goes through its middle. function Intersection (S1, S2 : Segment) return Point; function Intersection (L1, L2 : Line) return Point; -- Return the intersection of the two parameters. The result is either a -- simple point, or No_Point when they don't intersect, or -- Infinity_Points when the two parameters intersect on an infinity of -- points. function Intersect (C1, C2 : Circle) return Boolean; function Intersect (T1, T2 : Triangle) return Boolean; function Intersect (L : Line; C : Circle) return Boolean; function Intersect (R1, R2 : Rectangle) return Boolean; -- Whether the two parameters intersect function Inside (P : Point; S : Segment) return Boolean; function Inside (P : Point; L : Line) return Boolean; function Inside (P : Point; T : Triangle) return Boolean; function Inside (P : Point; Poly : Polygon) return Boolean; -- True if P is on the segment or line function Distance (From : Point; To : Point) return Distance_Type; function Distance (From : Point; To : Segment) return Distance_Type; function Distance (From : Point; To : Line) return Distance_Type; function Distance (From : Point; To : Polygon) return Distance_Type; -- Return the distance between P and the second parameter. function Centroid (Self : Polygon) return Point; -- Return the centroid of the polygon (aka center of gravity). function Area (Self : Triangle) return Distance_Type; function Area (Self : Polygon) return Distance_Type; -- Return the area of the (possibly non-convex) polygon. For the triangle, -- the area will be negative if the vertices are oriented clockwise function Same_Side (P1, P2 : Point; As : Segment) return Boolean; function Same_Side (P1, P2 : Point; As : Line) return Boolean; -- Whether the two points lay on the same side of the line overlapping -- the segment. It is slightly faster to use the Segment version private type Line is record A, B, C : Coordinate; end record; -- Representation for a line, through its equation, that -- is: Ax + By = C. See functions To_Line below if you define a line -- by a set of points. No_Point : constant Point := (Coordinate'First, Coordinate'First); No_Circle : constant Circle := (No_Point, Coordinate'First); Infinity_Points : constant Point := (Coordinate'Last, Coordinate'Last); end GNATCOLL.Geometry; gnatcoll-core-21.0.0/src/gnatcoll-remote-db.ads0000644000175000017500000001146313661715457021150 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package is used to keep a global configuration of servers. -- There are two almost identical parts: one defining static methods that -- can be used by other GNATCOLL packages (such as GNATCOLL.IO.Remote), -- and the second part that defines the actual configuration class, that -- needs to be implemented and registered by the user code. package GNATCOLL.Remote.Db is Invalid_Remote_Configuration : exception; --------------------------------------------------- -- The remote configuration interface definition -- --------------------------------------------------- type Remote_Db_Interface is interface; function Is_Configured (Config : Remote_Db_Interface; Nickname : String) return Boolean is abstract; -- Tell if a server with this name exists in the global configuration function Get_Server (Config : Remote_Db_Interface; Nickname : String) return Server_Access is abstract; -- Get the server from its nickname. function Nb_Mount_Points (Config : Remote_Db_Interface; Nickname : String) return Natural is abstract; -- Get the number of mount points defined for the server function Get_Mount_Point_Local_Root (Config : Remote_Db_Interface; Nickname : String; Index : Natural) return FS_String is abstract; -- Get the local mount point function Get_Mount_Point_Host_Root (Config : Remote_Db_Interface; Nickname : String; Index : Natural) return FS_String is abstract; -- Get the remote point. --------------------------------------------------------- -- Needs to be called before any of the static methods -- --------------------------------------------------------- procedure Define_Remote_Configuration (Config : access Remote_Db_Interface'Class); -- Defines the remote configuration that will be used for all remote access -- performed by GNATCOLL. ------------------------------------------------- -- Static methods used by GNATCOLL internally -- ------------------------------------------------- function Is_Configured (Nickname : String) return Boolean; -- Tell if a server with this name exists in the global configuration -- Raise Invalid_Remote_Config if no global configuration has been defined. function Get_Server (Nickname : String) return Server_Access; -- Get the server from its nickname. -- Raise Invalid_Remote_Config if no global configuration has been defined. function Nb_Mount_Points (Nickname : String) return Natural; -- Get the number of mount points defined for the server -- Raise Invalid_Remote_Config if no global configuration has been defined. function Get_Mount_Point_Local_Root (Nickname : String; Index : Natural) return FS_String; -- Get the local mount point -- Raise Invalid_Remote_Config if no global configuration has been defined. function Get_Mount_Point_Host_Root (Nickname : String; Index : Natural) return FS_String; -- Get the remote point. -- Raise Invalid_Remote_Config if no global configuration has been defined. end GNATCOLL.Remote.Db; gnatcoll-core-21.0.0/src/gnatcoll-projects.adb0000644000175000017500000136736113743647711021114 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2002-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ pragma Ada_2012; with Ada.Calendar; use Ada.Calendar; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Containers.Hashed_Sets; with Ada.Containers.Ordered_Sets; with Ada.Containers.Generic_Array_Sort; with Ada.Directories; with Ada.Exceptions; use Ada.Exceptions; with Ada.Strings; use Ada.Strings; with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Strings.Hash_Case_Insensitive; with Ada.Strings.Maps; use Ada.Strings.Maps; with Ada.Strings.Unbounded; with Ada.Text_IO; use Ada.Text_IO; with Ada.Unchecked_Conversion; with System; use System; with GNAT.Case_Util; use GNAT.Case_Util; with GNAT.Directory_Operations; use GNAT.Directory_Operations; with GNAT.Expect; use GNAT.Expect; with GNAT.Expect.TTY; use GNAT.Expect.TTY; with GNAT.Regpat; use GNAT.Regpat; with GNAT.OS_Lib; use GNAT.OS_Lib; with GNATCOLL.Projects.Normalize; use GNATCOLL.Projects.Normalize; with GNATCOLL.Traces; use GNATCOLL.Traces; with GNATCOLL.Utils; use GNATCOLL.Utils; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.VFS_Utils; use GNATCOLL.VFS_Utils; with GNATCOLL.Projects.Krunch; with GPR.Util; use GPR.Util; with GPR.Osint; with GPR.Opt; with GPR.Output; with GPR.Attr; use GPR.Attr; with GPR.Com; with GPR.Conf; use GPR.Conf; with GPR.Env; use GPR, GPR.Env; with GPR.Err; with GPR.Ext; with GPR.Names; use GPR.Names; with GPR.Part; with GPR.Proc; with GPR.PP; use GPR.PP; with GPR.Tree; use GPR.Tree; with GPR.Sinput; with GPR.Snames; use GPR.Snames; with GPR.Knowledge; with GPR.Sdefault; with DOM.Core.Nodes; with DOM.Core.Documents; with Input_Sources.File; with Sax.Readers; with Schema.Dom_Readers; package body GNATCOLL.Projects is package GU renames GNATCOLL.Utils; Me : constant Trace_Handle := Create ("Projects", Default => Off); Debug : constant Trace_Handle := Create ("Projects.Debug", Default => Off); Me_Gnat : constant Trace_Handle := Create ("Projects.GNAT", GNATCOLL.Traces.Off); Me_Aggregate_Support : constant Trace_Handle := Create ("Projects.Aggregate", Default => On); Me_SV : constant Trace_Handle := Create ("Projects.SV", Default => Off); -- Trace specific to Scenario/Untyped Variable computation. May create -- lots of output on relatively complex project trees, so makes sense -- to separate it from the main trace. Dummy_Suffix : constant String := ""; -- A dummy suffixes that is used for languages that have either no spec or -- no implementation suffix defined. Unknown_Importing_Projects : aliased constant Path_Name_Id_Array (1 .. 0) := (others => <>); -- A dummy array used while computing importing projects package Path_Sets is new Ada.Containers.Indefinite_Ordered_Sets (String); use Path_Sets; package Virtual_File_List is new Ada.Containers.Doubly_Linked_Lists (Element_Type => Virtual_File); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Scenario_Variable_Array, Scenario_Variable_Array_Access); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Untyped_Variable_Array, Untyped_Variable_Array_Access); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Project_Type'Class, Project_Type_Access); -------------------- -- Unchecked_Free -- -------------------- procedure Unchecked_Free (Arr : in out Project_Array_Access) is procedure Internal is new Ada.Unchecked_Deallocation (Project_Array, Project_Array_Access); begin Internal (Arr); end Unchecked_Free; package Project_Htables is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => Virtual_File, -- project path Element_Type => Project_Type, Hash => Full_Name_Hash, Equivalent_Keys => GNATCOLL.VFS."="); -- maps project paths (casing insensitive) to project types -- ??? This would not be needed if we could, in the prj* sources, associate -- user data with project nodes. type Source_File_Data; type Source_File_Data_Access is access all Source_File_Data; type Source_File_Data is record Project : Project_Type; File : GNATCOLL.VFS.Virtual_File; Lang : GPR.Name_Id; Source : GPR.Source_Id; Next : Source_File_Data_Access := null; end record; -- In some case, Lang might be set to Unknown_Language, if the file was -- set in the project (for instance through the Source_Files attribute), -- but no matching language was found. -- Next is only relevant when there may be more than one source with same -- base name. This can happen when root project is aggregate project, -- languages other than Ada are involved (i.e. C), or when list of -- languages is changed on the fly and same sources can be treated as those -- of different languages. In that case we can have multiple source files -- with same base name but different full names. Even sources with same -- full name can belong to different aggregated projects, so there are -- different Source_File_Data instances for such each such project; function Hash (File : GNATCOLL.VFS.Filesystem_String) return Ada.Containers.Hash_Type; function Equal (F1, F2 : GNATCOLL.VFS.Filesystem_String) return Boolean; pragma Inline (Hash, Equal); -- Either case sensitive or not, depending on the system package Names_Files is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => GNATCOLL.VFS.Filesystem_String, -- file base name Element_Type => Source_File_Data, Hash => Hash, Equivalent_Keys => Equal); -- maps for file base names to info about the file procedure Include_File (Map : in out Names_Files.Map; Key : GNATCOLL.VFS.Filesystem_String; Elem : Source_File_Data); -- If there is no file with same base name in map, adds the file as the -- first element in corresponding list. Otherwise adds it to the list. procedure Clean_Up (Map : in out Names_Files.Map); -- Clean up possibly existing lists of files with similar base names, -- then Clear the map itself. function Hash (Node : Project_Node_Id) return Ada.Containers.Hash_Type; pragma Inline (Hash); package Project_Sets is new Ada.Containers.Hashed_Sets (Element_Type => Project_Node_Id, Hash => Hash, Equivalent_Elements => "="); type Directory_Dependency is (Direct, As_Parent); -- The way a directory belongs to the project: either as a direct -- dependency, or because one of its subdirs belong to the project, or -- doesn't belong at all. package Directory_Statuses is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => GNATCOLL.VFS.Filesystem_String, -- Directory path Element_Type => Directory_Dependency, Hash => Hash, Equivalent_Keys => Equal); -- Whether a directory belongs to a project use Project_Htables, Extensions_Languages, Names_Files; use Directory_Statuses; type Project_Tree_Data (Is_Aggregated : Boolean) is record Env : Project_Environment_Access; Tree : GPR.Project_Node_Tree_Ref; View : GPR.Project_Tree_Ref; -- The description of the trees Status : Project_Status := From_File; Root : Project_Type := No_Project; -- The root of the project hierarchy Directories : Directory_Statuses.Map; -- Index on directory name -- ??? might not be needed anymore, using the hash tables already -- in GPR.* Timestamp : Ada.Calendar.Time := GNATCOLL.Utils.No_Time; -- Time when we last parsed the project from the disk case Is_Aggregated is when False => Sources : Names_Files.Map; -- Index on base source file names, returns information about -- the file. Objects_Basename : Names_Files.Map; -- The basename (with no extension or directory) of the object -- files. This is used to quickly filter out the relevant object -- or library files when an object directory is shared amongst -- multiple projects. This table does not point to the actual -- location of the object files, which might be in an extending -- project. It only provides a quick way to filter out irrelevant -- object files. Projects : Project_Htables.Map; -- Index on project paths. This table is filled when the project -- is loaded. when True => null; end case; end record; procedure Free (Self : in out Project_Tree_Data_Access); -- Free memory used by Self. function Get_View (Tree : GPR.Project_Tree_Ref; Path : Path_Name_Type) return GPR.Project_Id; -- Return the project view for the project Name type External_Variable_Callback is access procedure (Variable : Project_Node_Id; Prj : Project_Node_Id; Pkg : Project_Node_Id; Project : Project_Type); -- Called for a typed variable declaration that references an external -- variable in GPR. type Get_Directory_Path_Callback is access function (Project : GPR.Project_Id) return Path_Information; -- Called to get the directory path the Get_Directory function must -- return the Virtual_File function Get_Directory (Project : Project_Type; Callback : Get_Directory_Path_Callback) return Virtual_File; -- return the Virtual_File generated from the callback return. -- If callback returns a 0 length Path_Information then function returns -- the project object directory. -- If project not accessible return No_File. function Variable_Value_To_List (Project : Project_Type; Value : Variable_Value) return GNAT.Strings.String_List_Access; -- Allocate a new String_List that contains the strings stored in Value. -- Result must be freed by caller. procedure For_Each_Project_Node (Tree : GPR.Project_Node_Tree_Ref; Root : Project_Node_Id; Callback : access procedure (Tree : GPR.Project_Node_Tree_Ref; Node : Project_Node_Id)); -- Iterate over all projects in the tree. -- They are each returned once, the root project first and then all its -- imported projects. -- As opposed to For_Every_Project_Imported, this iteration in based on the -- project tree, and therefore can be used before the project view has been -- computed. -- This includes projects extended by Root. -- The order is: -- root project, project, extended_project_of_project,... function Default_Spec_Suffix (Self : Project_Environment'Class; Language_Name : String) return String; function Default_Body_Suffix (Self : Project_Environment'Class; Language_Name : String) return String; -- Return the default extensions for a given language, as registered -- through Register_Default_Language_Extension; procedure For_Each_External_Variable_Declaration (Root_Project : Project_Type; Recursive : Boolean; Callback : External_Variable_Callback); -- Iterate other all the typed variable declarations that reference -- external variables in Project (or one of its imported projects if -- Recursive is true). -- Callback is called for each of them. procedure Append (Self : in out Path_Name_Array; Path : GPR.Path_Name_Type); -- Resize Self if needed, and append a new value procedure Reset (Tree : in out Project_Tree'Class; Env : Project_Environment_Access); -- Make sure the Tree data has been created and initialized function Substitute_Dot (Unit_Name : String; Dot_Replacement : String) return String; -- Replace the '.' in unit_name with Dot_Replacement procedure Compute_Importing_Projects (Project : Project_Type'Class; Root_Project : Project_Type'Class); -- Compute the list of all projects that import, possibly indirectly, -- Project. procedure Reset_View (Tree : Project_Tree'Class); -- Clear internal tables for the view function String_Elements (Data : Project_Tree_Data_Access) return GPR.String_Element_Table.Table_Ptr; pragma Inline (String_Elements); -- Return access to the various tables that contain information about the -- project function Get_String (Id : GPR.File_Name_Type) return String; function Get_String (Id : GPR.Path_Name_Type) return String; pragma Inline (Get_String); -- Return the string in Name -- Same as GPR.Get_Name_String, but return "" in case of -- failure, instead of raising Assert_Failure. function Create_Flags (On_Error : GPR.Error_Handler; Require_Sources : Boolean := True; Ignore_Missing_With : Boolean := False; Report_Missing_Dirs : Boolean := True) return Processing_Flags; -- Return the flags to pass to the project manager in the context of GPS. -- Require_Sources indicates whether each language must have sources -- attached to it. function Length (Tree : GPR.Project_Tree_Ref; List : GPR.String_List_Id) return Natural; -- Return the number of elements in the list function Attribute_Value (Project : Project_Type; Attribute : String; Index : String := ""; Use_Extended : Boolean := False) return Variable_Value; -- Internal version of Attribute_Value function Has_Attribute (Project : Project_Type; Attribute : String; Index : String := "") return Boolean; -- Internal version of Has_Attribute function Attribute_Indexes (Project : Project_Type; Attribute : String; Use_Extended : Boolean := False) return GNAT.Strings.String_List; -- Internal version of Attribute_Indexes procedure Reset_View (Self : in out Project_Data'Class); -- Reset and free the internal data of the project view procedure Compute_Scenario_Variables (Tree : Project_Tree_Data_Access; Recursive : Boolean := True; Errors : Error_Report := null); -- Compute (and cache) the whole list of scenario variables for the -- project tree. -- This also ensures that each external reference actually exists function Source_File_Data_To_Info (S : Source_File_Data) return File_Info; -- Converts from one structure to the other procedure Compute_Imported_Projects (Project : Project_Type'Class); -- Compute and cache the list of projects imported by Project. -- Nothing is done if this is already known. -- This also include projects extended by Project. -- The order is -- root_project, project, project_extended_by_project, ... function Delete_File_Suffix (Filename : GNATCOLL.VFS.Filesystem_String; Project : Project_Type) return Natural; -- Return the last index in Filename before the beginning of the file -- suffix. Suffixes are searched independently from the language. -- If not matching suffix is found in project, the returned value will -- simply be Filename'Last. procedure Internal_Load (Tree : in out Project_Tree'Class; Root_Project_Path : GNATCOLL.VFS.Virtual_File; Errors : Projects.Error_Report; Report_Syntax_Errors : Boolean; Project : out Project_Node_Id; Packages_To_Check : GNAT.Strings.String_List_Access := All_Packs; Recompute_View : Boolean := True; Test_With_Missing_With : Boolean := True; Report_Missing_Dirs : Boolean := True; Implicit_Project : Boolean); -- Internal implementation of load. This doesn't reset the tree at all, -- but will properly setup the GNAT project manager so that error messages -- are redirected and fatal errors do not kill GPS. -- If Test_With_Missing_With is True, first test with ignoring unresolved -- "with" statement, in case we need to first parse the gnatlist attribute. procedure Parse_Source_Files (Self : in out Project_Tree); -- Find all the source files for the project, and cache them. -- At the same time, check that the gnatls attribute is coherent between -- all projects and subprojects, and memorize the sources in the -- hash-table. function Info (Tree : Project_Tree_Data_Access; File : GNATCOLL.VFS.Virtual_File) return File_Info; -- Internal version of Info procedure Create_Project_Instances (Self : Project_Tree'Class; Tree_For_Map : Project_Tree'Class; With_View : Boolean); function Instance_From_Node (Self : Project_Tree'Class; Tree_For_Map : Project_Tree'Class; Node : Project_Node_Id) return Project_Type; -- Create all instances of Project_Type for the loaded projects. -- Instances are put in Htable of Tree_For_Map parameter. -- This also resets the internal data for the view. function Handle_Subdir (Project : Project_Type; Id : GPR.Path_Name_Type; Xref_Dirs : Boolean) return Filesystem_String; -- Adds the object subdirectory to Id if one is defined function Kind_To_Part (Source : Source_Id) return Unit_Parts; -- Converts from Source.Kind to Unit_Parts function Set_Path_From_Gnatls_Attribute (Project : Project_Id; Tree : Project_Tree'Class; Errors : Error_Report := null) return Boolean; -- Look at the gnatls attribute, if defined, and update the predefined -- path if needed. -- Return True if the path was updated. procedure Put (Self : in out Pretty_Printer'Class; Project : Project_Node_Id; In_Tree : GPR.Project_Node_Tree_Ref; Id : Project_Id := GPR.No_Project; Increment : Positive := 3; Eliminate_Empty_Case_Constructions : Boolean := False); -- Internal version of Put, acting directly on the low-level structures Specific_Attributes_Registered : Boolean := False; procedure Register_Specific_Attributes; -- Register specific attributes like IDE'Artifact_Dir but only once per -- program run. Second attempt at registering the attribute leads to an -- error in libgpr. Host_Targets_List : GPR.Knowledge.String_Lists.List := GPR.Knowledge.String_Lists.Empty_List; Host_Targets_List_Set : Boolean := False; procedure Set_Host_Targets_List; -- Populates the list of host targets that include the host target itself -- and may as well have corresponding fallback targets. It also parses -- targetset.xml and populates Normalisation_Dictionary (see below). type Targetset_Info is record Canonical_Name : Ada.Strings.Unbounded.Unbounded_String; Regexp_Imgs : GPR.Knowledge.String_Lists.List; end record; function "<" (L, R : Targetset_Info) return Boolean is (Ada.Strings.Unbounded."<" (L.Canonical_Name, R.Canonical_Name)); package Targetset_Info_Set is new Ada.Containers.Ordered_Sets (Targetset_Info); Normalization_Dictionary : Targetset_Info_Set.Set; function Normalize_Target_Name (Target_Name : String) return String; -- Normalizes name of target against Normalization_Dictionary. If no match -- is found return Target_Name as is. ----------- -- Lists -- ----------- type String_List_Iterator is record Current : Project_Node_Id; -- pointer to N_Literal_String or N_Expression end record; function Done (Iter : String_List_Iterator) return Boolean; -- Return True if Iter is past the end of the list of strings function Next (Tree : GPR.Project_Node_Tree_Ref; Iter : String_List_Iterator) return String_List_Iterator; -- Return the next item in the list function Data (Tree : GPR.Project_Node_Tree_Ref; Iter : String_List_Iterator) return GPR.Name_Id; -- Return the value pointed to by Iter. -- This could be either a N_String_Literal or a N_Expression node in the -- first case. -- The second case only works if Iter points to N_String_Literal. function Value_Of (Tree : GPR.Project_Node_Tree_Ref; Var : Scenario_Variable) return String_List_Iterator; -- Return an iterator over the possible values of the variable ------------ -- Errors -- ------------ generic Tree : Project_Tree'Class; procedure Mark_Project_Error (Project : Project_Id; Is_Warning : Boolean); -- Handler called when the project parser finds an error. -- Mark_Project_Incomplete should be true if any error should prevent the -- edition of project properties graphically. ------------------ -- Kind_To_Part -- ------------------ function Kind_To_Part (Source : Source_Id) return Unit_Parts is begin if Source = null then return Unit_Separate; end if; case Source.Kind is when Spec => return Unit_Spec; when Impl => return Unit_Body; when Sep => return Unit_Separate; end case; end Kind_To_Part; ------------------------ -- Mark_Project_Error -- ------------------------ procedure Mark_Project_Error (Project : Project_Id; Is_Warning : Boolean) is P : Project_Type; pragma Warnings (Off, P); -- ??? Without the pragma Warnings (Off), when compiling with -gnatwae, -- we get this error: -- warning: variable "P" is assigned but never read begin if not Is_Warning then if Project = GPR.No_Project then if Tree.Root_Project /= No_Project then declare Iter : Inner_Project_Iterator := Start (Tree.Root_Project); begin while Current (Iter) /= No_Project loop Current (Iter).Data.View_Is_Complete := False; Next (Iter); end loop; end; end if; else if Tree.Data.Root /= No_Project then P := Project_Type (Project_From_Name (Tree.Data, Project.Name)); P.Data.View_Is_Complete := False; end if; end if; end if; end Mark_Project_Error; --------------- -- Tree_View -- --------------- function Tree_View (P : Project_Type'Class) return GPR.Project_Tree_Ref is begin return P.Data.Tree.View; end Tree_View; --------------- -- Tree_Tree -- --------------- function Tree_Tree (P : Project_Type'Class) return GPR.Project_Node_Tree_Ref is begin return P.Data.Tree.Tree; end Tree_Tree; --------------------- -- String_Elements -- --------------------- function String_Elements (Data : Project_Tree_Data_Access) return GPR.String_Element_Table.Table_Ptr is begin return Data.View.Shared.String_Elements.Table; end String_Elements; ------------ -- Length -- ------------ function Length (Tree : GPR.Project_Tree_Ref; List : GPR.String_List_Id) return Natural is L : String_List_Id := List; Count : Natural := 0; begin while L /= Nil_String loop Count := Count + 1; L := Tree.Shared.String_Elements.Table (L).Next; end loop; return Count; end Length; ---------------- -- Get_String -- ---------------- function Get_String (Id : GPR.File_Name_Type) return String is begin if Id = GPR.No_File then return ""; end if; return Get_Name_String (Id); exception when E : others => Trace (Me, E); return ""; end Get_String; function Get_String (Id : GPR.Path_Name_Type) return String is begin if Id = GPR.No_Path then return ""; end if; return Get_Name_String (Id); exception when E : others => Trace (Me, E); return ""; end Get_String; ---------- -- Name -- ---------- function Name (Project : Project_Type) return String is begin if Project.Data = null then return "default"; elsif Get_View (Project) /= GPR.No_Project then return Get_String (Get_View (Project).Display_Name); else return Get_String (GPR.Tree.Name_Of (Project.Data.Node, Project.Tree_Tree)); end if; end Name; ------------------ -- Project_Path -- ------------------ function Project_Path (Project : Project_Type; Host : String := Local_Host) return GNATCOLL.VFS.Virtual_File is View : constant GPR.Project_Id := Get_View (Project); begin if Project.Data = null or else Project.Data.Node = Empty_Project_Node then return GNATCOLL.VFS.No_File; elsif View = GPR.No_Project then -- View=GPR.No_Project case needed for the project wizard return To_Remote (Create (+Get_String (Path_Name_Of (Project.Data.Node, Project.Tree_Tree))), Host); else return To_Remote (Create (+Get_String (View.Path.Display_Name)), Host); end if; end Project_Path; ----------------- -- Source_Dirs -- ----------------- function Source_Dirs (Project : Project_Type; Recursive : Boolean := False; Include_Externally_Built : Boolean := True) return GNATCOLL.VFS.File_Array is Current_Dir : constant Filesystem_String := Get_Current_Dir; Iter : Inner_Project_Iterator; Count : Natural := 0; P : Project_Type; View : Project_Id; Src : String_List_Id; Aggregated : Aggregated_Project_List; Aggregated_Dirs : File_Array_Access := null; begin if Is_Aggregate_Project (Project) and then Recursive then Aggregated := Project.Data.View.Aggregated_Projects; while Aggregated /= null loop Append (Aggregated_Dirs, Source_Dirs (Project_From_Path (Project.Data.Tree, Aggregated.Path), Recursive => True, Include_Externally_Built => Include_Externally_Built)); Aggregated := Aggregated.Next; end loop; return Aggregated_Dirs.all; end if; Iter := Start (Project, Recursive); loop P := Current (Iter); exit when P = No_Project; View := Get_View (P); exit when View = GPR.No_Project; if Include_Externally_Built or else not Externally_Built (P) then Count := Count + Length (Project.Tree_View, View.Source_Dirs); end if; Next (Iter); end loop; declare Sources : File_Array (1 .. Count); Index : Natural := Sources'First; begin Iter := Start (Project, Recursive); loop P := Current (Iter); exit when P = No_Project; View := Get_View (P); exit when View = GPR.No_Project; if Include_Externally_Built or else not Externally_Built (P) then Src := View.Source_Dirs; while Src /= Nil_String loop Sources (Index) := Create (Normalize_Pathname (+Get_String (String_Elements (P.Data.Tree) (Src).Display_Value), Current_Dir, Resolve_Links => False)); Ensure_Directory (Sources (Index)); Index := Index + 1; Src := String_Elements (P.Data.Tree) (Src).Next; end loop; end if; Next (Iter); end loop; return Sources (1 .. Index - 1); end; end Source_Dirs; ------------------- -- Handle_Subdir -- ------------------- function Handle_Subdir (Project : Project_Type; Id : GPR.Path_Name_Type; Xref_Dirs : Boolean) return Filesystem_String is View : constant Project_Id := Get_View (Project); Env : constant Project_Environment_Access := Project.Data.Tree.Env; Path : constant Filesystem_String := Name_As_Directory (+Get_String (Id)); begin if not Xref_Dirs or else Xrefs_Subdir (Env.all)'Length = 0 or else View.Externally_Built then return Path; elsif GPR.Subdirs /= null then return Name_As_Directory (Path (Path'First .. Path'Last - GPR.Subdirs.all'Length - 1) & Xrefs_Subdir (Env.all)); else return Path & Name_As_Directory (Xrefs_Subdir (Env.all)); end if; end Handle_Subdir; ---------------- -- Object_Dir -- ---------------- function Object_Dir (Project : Project_Type) return GNATCOLL.VFS.Virtual_File is View : constant Project_Id := Get_View (Project); begin if View /= GPR.No_Project and then View.Object_Directory /= No_Path_Information then return Create (Handle_Subdir (Project, View.Object_Directory.Display_Name, False)); else return GNATCOLL.VFS.No_File; end if; end Object_Dir; ------------------- -- Artifacts_Dir -- ------------------- function Artifacts_Dir (Project : Project_Type) return GNATCOLL.VFS.Virtual_File is D : GNATCOLL.VFS.Virtual_File; Att : constant Attribute_Pkg_String := Build ("IDE", "Artifacts_Dir"); begin if Project.Data.Tree.Env.IDE_Mode and then Project.Has_Attribute (Att) and then Attribute_Value (Project, Att) /= "" then D := Create_From_Base (+Attribute_Value (Project, Att), Project.Project_Path.Dir_Name); Ensure_Directory (D); return D; end if; if Project.Object_Dir /= GNATCOLL.VFS.No_File then return Project.Object_Dir; end if; Trace (Me, Project.Name & " does not have an object dir"); D := Create (Project.Project_Path.Dir_Name & Project.Data.Tree.Env.Object_Subdir); Ensure_Directory (D); if Is_Writable (D) then return D; else Trace (Me, "Directory '" & D.Display_Full_Name & "' is not writable"); return GNATCOLL.VFS.No_File; end if; end Artifacts_Dir; ----------------- -- Object_Path -- ----------------- function Object_Path (Project : Project_Type; Recursive : Boolean := False; Including_Libraries : Boolean := False; Xrefs_Dirs : Boolean := False; Exclude_Externally : Boolean := False) return File_Array is View : constant Project_Id := Get_View (Project); begin if View = GPR.No_Project then return (1 .. 0 => <>); elsif Recursive then declare Iter : Project_Iterator := Start (Project, Recursive); Result : File_Array_Access; P : Project_Type; begin loop P := Current (Iter); exit when P = No_Project or else P.Get_View = GPR.No_Project; Prepend (Result, P.Object_Path (Recursive => False, Including_Libraries => Including_Libraries, Xrefs_Dirs => Xrefs_Dirs, Exclude_Externally => Exclude_Externally)); Next (Iter); end loop; return R : constant File_Array := Result.all do Unchecked_Free (Result); end return; end; elsif Including_Libraries and then View.Library and then View.Library_ALI_Dir /= No_Path_Information then -- Object_Directory is in fact always defined for projects read from -- files (if unspecified in the user's project, it defaults to the -- projects' own directory). -- For externally_built library projects, however, it should not be -- taken into account. if View.Externally_Built and then Exclude_Externally then return (1 .. 0 => <>); elsif View.Object_Directory = No_Path_Information or else View.Externally_Built then return (1 => Create (Handle_Subdir (Project, View.Library_ALI_Dir.Display_Name, Xrefs_Dirs))); else return (Create (Handle_Subdir (Project, View.Object_Directory.Display_Name, Xrefs_Dirs)), Create (Handle_Subdir (Project, View.Library_ALI_Dir.Display_Name, Xrefs_Dirs))); end if; elsif View.Object_Directory /= No_Path_Information then return (1 => Create (Handle_Subdir (Project, View.Object_Directory.Display_Name, Xrefs_Dirs))); else return (1 .. 0 => <>); end if; end Object_Path; ------------------- -- Library_Files -- ------------------- procedure Library_Files (Self : Project_Type; Recursive : Boolean := False; Including_Libraries : Boolean := True; Xrefs_Dirs : Boolean := False; ALI_Ext : GNATCOLL.VFS.Filesystem_String := ".ali"; Include_Predefined : Boolean := False; List : in out Library_Info_List'Class; Exclude_Overridden : Boolean := True) is Tmp : File_Array_Access; Prj_Iter : Project_Iterator; Current_Project : Project_Type; Info_Cursor : Names_Files.Cursor; Re : Pattern_Matcher_Access; package Virtual_File_Sets is new Ada.Containers.Hashed_Sets (Element_Type => Virtual_File, Hash => Full_Name_Hash, Equivalent_Elements => "=", "=" => "="); use Virtual_File_Sets; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Pattern_Matcher, Pattern_Matcher_Access); function Get_Base_Name (F : Virtual_File) return Filesystem_String; -- Return the base name of the argument. If ALI_Ext was a regular -- expression, this function simply strips the file extension -- (everything after and including the last dot in the file name). -- Otherwise, the suffix ALI_Ext is removed from the file name. function Is_Extending_All (P : Project_Type) return Boolean is (Is_Extending_All (P.Data.Node, P.Data.Tree.Tree)); function Find_Ada_In_Subtree (Map : Names_Files.Map; Key : GNATCOLL.VFS.Filesystem_String; Root : Project_Type) return Names_Files.Cursor; function Find_C_In_Subtree (Map : Names_Files.Map; Key : GNATCOLL.VFS.Filesystem_String; Root : Project_Type) return Names_Files.Cursor; -- Searches for Source_File_Data with given base name and a project from -- a project subtree that starts from Root. -- If the resulting Source_File_Data is not the first one in the list, -- it is placed in Local_Obj_Map and returned Cursor points to it. -- Local_Obj_Map must be cleared after each object file is processed. -- -- ??? This function seems to be the same as Create_From_Project. procedure Process_Project (Project : Project_Type); -- Process Project and append to List all relevant ALI files Local_Obj_Map : Names_Files.Map; ------------------- -- Get_Base_Name -- ------------------- function Get_Base_Name (F : Virtual_File) return Filesystem_String is begin if Re = null then return F.Base_Name (ALI_Ext); else return F.Base_Name (F.File_Extension); end if; end Get_Base_Name; ------------------------- -- Find_Ada_In_Subtree -- ------------------------- function Find_Ada_In_Subtree (Map : Names_Files.Map; Key : GNATCOLL.VFS.Filesystem_String; Root : Project_Type) return Names_Files.Cursor is Cur : Names_Files.Cursor; Iter : Project_Iterator; SFD : Source_File_Data; begin Cur := Map.Find (Key); if Cur = Names_Files.No_Element then -- No object files with same base name expected for any project. return Cur; end if; SFD := Element (Cur); loop if not (Get_String (SFD.Lang) in "c" | "cpp") then Iter := Start (Extending_Project (Root, True)); while Current (Iter) /= No_Project loop if Current (Iter) = SFD.Project then -- Creating a temporary element to point to. Local_Obj_Map.Include (Key, SFD); return Local_Obj_Map.First; end if; Next (Iter); end loop; end if; exit when SFD.Next = null; SFD := SFD.Next.all; end loop; return Names_Files.No_Element; end Find_Ada_In_Subtree; ----------------------- -- Find_C_In_Subtree -- ----------------------- function Find_C_In_Subtree (Map : Names_Files.Map; Key : GNATCOLL.VFS.Filesystem_String; Root : Project_Type) return Names_Files.Cursor is Cur : Names_Files.Cursor; SFD : Source_File_Data; Extended_P : Project_Type; begin Cur := Map.Find (Key); if Cur = Names_Files.No_Element then -- No object files with same base name expected for any project. return Cur; end if; SFD := Element (Cur); -- Use a standard iterator (and remove aggregated projects ourselves) -- instead of an inner iterator, so that library projects aggregated -- in a library aggregate are also considered loop if Get_String (SFD.Lang) in "c" | "cpp" then -- We can have as much c/c++ files with same name as possible. -- So what we need to do is only iterate through extended -- projects to check whether the current file belongs to them, -- but not through the whole project subtree, since we can find -- absolutely unrelated homonyms. Extended_P := Extending_Project (Root, True); loop if Extended_P = SFD.Project then Local_Obj_Map.Include (Key, SFD); return Local_Obj_Map.First; end if; Extended_P := Extended_Project (Extended_P); exit when Extended_P = No_Project; end loop; end if; exit when SFD.Next = null; SFD := SFD.Next.all; end loop; return Names_Files.No_Element; end Find_C_In_Subtree; Seen, Added : Virtual_File_Sets.Set; --------------------- -- Process_Project -- --------------------- procedure Process_Project (Project : Project_Type) is Objects : constant File_Array := Object_Path (Project, Recursive => False, Including_Libraries => Including_Libraries, Xrefs_Dirs => Xrefs_Dirs); Dir : Virtual_File; Should_Append : Boolean; Lowest_Project : Project_Type; begin if Objects'Length = 0 or else Seen.Contains (Objects (Objects'First)) then return; end if; -- Only look at the first object directory (which is either -- object_dir, if it exists, or library_dir, if it exists). -- We never need to look at both of them. Dir := Objects (Objects'First); Seen.Include (Dir); Trace (Me, "Library_Files, reading dir " & Dir.Display_Full_Name); Tmp := Read_Dir (Dir); for F in Tmp'Range loop if (Re /= null and then Match (Re.all, +Tmp (F).Base_Name)) or else (Re = null and then Tmp (F).Has_Suffix (ALI_Ext)) then declare B : constant Filesystem_String := Get_Base_Name (Tmp (F)); B_Last : Integer := B'Last; Dot : Integer; P : Project_Type; begin Info_Cursor := Find_Ada_In_Subtree (Self.Data.Tree_For_Map.Objects_Basename, B, Self); if not Has_Element (Info_Cursor) then -- Special case for C files: the library file is -- file.c.gli -- instead of file.ali as we would have in Ada Dot := B'Last; while Dot >= B'First and then B (Dot) /= '.' loop Dot := Dot - 1; end loop; if Dot > B'First then B_Last := Dot - 1; Info_Cursor := Find_C_In_Subtree (Self.Data.Tree_For_Map.Objects_Basename, B (B'First .. B_Last), Project); end if; end if; -- An LI file is taking into account if: -- * it has a name that is known in this -- project (and thus matches one source file). -- This is a quick filter. -- * AND it is not overridden in one of the -- extending projects. -- This test is not necessary if we don't want -- to filter out overridden LI files if not Has_Element (Info_Cursor) then if Active (Me) then Trace (Me, "Library_Files not including : " & Display_Base_Name (Tmp (F)) & " (which is for unknown project)"); end if; Should_Append := False; elsif not Exclude_Overridden then Lowest_Project := Element (Info_Cursor).Project; Should_Append := Lowest_Project /= No_Project; else -- P is the candidate project that contains the -- LI file, but the latter might be overridden -- in any project extending P. P := Element (Info_Cursor).Project; -- This will contain the most-extending project -- that contains a homonym of the LI file Lowest_Project := P; P := P.Extending_Project; For_Each_Extending_Project : while P /= No_Project loop declare Objs : constant File_Array := P.Object_Path (Recursive => False, Including_Libraries => Including_Libraries, Xrefs_Dirs => Xrefs_Dirs); begin for Obj in Objs'Range loop if Create_From_Base (Tmp (F).Base_Name, Objs (Obj).Full_Name.all).Is_Regular_File then if Active (Me) then Trace (Me, "overridden in project " & P.Name); end if; Lowest_Project := P; exit; end if; end loop; end; P := P.Extending_Project; end loop For_Each_Extending_Project; -- Since we are traversing each directory only once, we -- cannot check that Lowest_Project is Project. Instead, -- we need to check with the object dirs. Should_Append := Lowest_Project = P; if not Should_Append then declare Lowest_Objs : constant File_Array := Lowest_Project.Object_Path (Recursive => False, Including_Libraries => Including_Libraries, Xrefs_Dirs => Xrefs_Dirs); begin for Ob in Lowest_Objs'Range loop Should_Append := Lowest_Objs (Ob) = Dir; exit when Should_Append; end loop; end; end if; end if; end; -- Take into account Recursive parameter to decide -- whether the library file belongs to Self when -- Recursive = False, in case several projects share -- the same object directory. We can only do that if -- the project isn't extended though. if Should_Append and then not Recursive and then Lowest_Project.Extending_Project = No_Project then Should_Append := Lowest_Project = Self; end if; if Has_Element (Info_Cursor) and then Added.Contains (Element (Info_Cursor).File) then Trace (Me, "Library_Files not including : " & Display_Base_Name (Tmp (F)) & " (which is overwritten in extends all project)"); elsif Should_Append then List.Append (Library_Info' (Library_File => Tmp (F), LI_Project => new Project_Type' (Lowest_Project), Non_Aggregate_Root_Project => new Project_Type'(Self), Source => new File_Info' (Source_File_Data_To_Info (Element (Info_Cursor))))); elsif Has_Element (Info_Cursor) and then Is_Extending_All (Project) then -- Corresponding source is not from current project, but -- current project is an extends all project, so any -- library file for a source of any project of the subtree -- belongs to current project. List.Append (Library_Info' (Library_File => Tmp (F), LI_Project => new Project_Type' (Lowest_Project), Non_Aggregate_Root_Project => new Project_Type'(Self), Source => new File_Info' (Source_File_Data_To_Info (Element (Info_Cursor))))); if Exclude_Overridden then -- Also so that we do not include corresponding -- overridden ALI file from the corresponding project, -- we need to store its name explicitly. Added.Include (Element (Info_Cursor).File); end if; elsif Active (Me) and then Has_Element (Info_Cursor) then Trace (Me, "Library_Files not including : " & Display_Base_Name (Tmp (F)) & " (which is for project " & Element (Info_Cursor).Project.Name & ")"); end if; Local_Obj_Map.Clear; end if; end loop; Unchecked_Free (Tmp); exception when VFS_Directory_Error => Trace (Me, "Couldn't open the directory " & Dir.Display_Full_Name); end Process_Project; begin if Is_Aggregate_Project (Self) then Increase_Indent (Me, "Library file for an aggregate project"); declare Aggregated : Aggregated_Project_List; P : Project_Type; begin -- processing aggregated project hierarchies Aggregated := Self.Data.View.Aggregated_Projects; while Aggregated /= null loop P := Project_Type (Project_From_Path (Self.Data.Tree, Aggregated.Path)); Library_Files (Self => P, Recursive => Recursive, Including_Libraries => Including_Libraries, Xrefs_Dirs => Xrefs_Dirs, ALI_Ext => ALI_Ext, Include_Predefined => False, List => List, Exclude_Overridden => Exclude_Overridden); Aggregated := Aggregated.Next; end loop; end; Decrease_Indent (Me, "Done Library file for aggregate project"); return; end if; if Active (Me) then Increase_Indent (Me, "Library_Files for project " & Self.Project_Path.Display_Full_Name); end if; -- An extended project logically does not contain any ALI file when in -- non recursive mode, so we simply do not look for them. if not Recursive and then Self.Extending_Project /= No_Project then return; end if; if ALI_Ext (ALI_Ext'First) = '^' then Re := new Pattern_Matcher'(Compile (+ALI_Ext)); end if; -- We do not call Object_Path with Recursive=>True, but instead -- iterate explicitly on the projects so that we can control which of -- the object_dir or library_dir we want to use *for each project*. -- -- We always look for projects recursively: when the user specified -- Recursive=>False, we still want to look at the extended projects -- of Self, so that all ALI files are associated with the lowest -- extending project. If the user specified Recursive=>False and -- Self is an extended project, we have already exited this procedure. -- -- We are seeing extending projects before extended projects Prj_Iter := Self.Start_Reversed (Recursive => True); loop Current_Project := Current (Prj_Iter); exit when Current_Project = No_Project; -- Ignore projects that the user is not interested in (i.e. in -- non recursive mode, ignore all non-extended projects) if Recursive or else Current_Project = Self or else Current_Project.Extending_Project (Recurse => True) = Self then if Active (Me) then Trace (Me, "Current project: " & Current_Project.Project_Path.Display_Full_Name); end if; Process_Project (Current_Project); end if; Next (Prj_Iter); end loop; if Include_Predefined then declare Predef : constant File_Array_Access := Self.Data.Tree.Env.Predefined_Object_Path; Tmp : File_Array_Access; begin for P in Predef'Range loop if not Seen.Contains (Predef (P)) and then Predef (P).Is_Directory then Seen.Include (Predef (P)); Tmp := Read_Dir (Predef (P)); for F in Tmp'Range loop if (Re /= null and then Match (Re.all, +Tmp (F).Base_Name)) or else (Re = null and then Tmp (F).Has_Suffix (ALI_Ext)) then List.Append (Library_Info' (Library_File => Tmp (F), Non_Aggregate_Root_Project => null, LI_Project => null, Source => null)); end if; end loop; Unchecked_Free (Tmp); end if; end loop; end; end if; Unchecked_Free (Re); Added.Clear; if Active (Me) then Decrease_Indent (Me, "Done library files"); end if; end Library_Files; ------------------- -- Library_Files -- ------------------- function Library_Files (Self : Project_Type; Recursive : Boolean := False; Including_Libraries : Boolean := True; Xrefs_Dirs : Boolean := False; ALI_Ext : GNATCOLL.VFS.Filesystem_String := ".ali"; Include_Predefined : Boolean := False; Exclude_Overridden : Boolean := True) return GNATCOLL.VFS.File_Array_Access is use Library_Info_Lists; List : Library_Info_List; C : Library_Info_Lists.Cursor; Result : File_Array_Access; Index : Integer; begin Library_Files (Self, Recursive => Recursive, Including_Libraries => Including_Libraries, Xrefs_Dirs => Xrefs_Dirs, Include_Predefined => Include_Predefined, ALI_Ext => ALI_Ext, Exclude_Overridden => Exclude_Overridden, List => List); Result := new File_Array (1 .. Integer (Length (List))); Index := Result'First; C := List.First; while Has_Element (C) loop Result (Index) := Element (C).Library_File; Index := Index + 1; Next (C); end loop; List.Clear; return Result; end Library_Files; ---------- -- Free -- ---------- procedure Free (Self : in out File_Info_Access) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (File_Info, File_Info_Access); begin Unchecked_Free (Self); end Free; ---------- -- Free -- ---------- procedure Free (Self : in out Library_Info) is begin Free (Self.Source); Unchecked_Free (Self.LI_Project); Unchecked_Free (Self.Non_Aggregate_Root_Project); end Free; -------------- -- Clean_Up -- -------------- procedure Clean_Up (Map : in out Names_Files.Map) is El : Source_File_Data; Tmp_El : Source_File_Data_Access; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Source_File_Data, Source_File_Data_Access); begin for C in Map.Iterate loop El := Names_Files.Element (C); while El.Next /= null loop Tmp_El := El.Next; El.Next := El.Next.Next; Unchecked_Free (Tmp_El); end loop; end loop; Map.Clear; end Clean_Up; ----------- -- Clear -- ----------- overriding procedure Clear (Self : in out Library_Info_List) is L : Library_Info; C : Library_Info_Lists.Cursor := Self.First; begin while Library_Info_Lists.Has_Element (C) loop L := Library_Info_Lists.Element (C); Free (L); Library_Info_Lists.Next (C); end loop; Library_Info_Lists.Clear (Library_Info_Lists.List (Self)); -- inherited end Clear; -------------------------- -- Direct_Sources_Count -- -------------------------- function Direct_Sources_Count (Project : Project_Type) return Natural is begin -- ??? Should directly use the size of Source_Files, since this is now -- precomputed when the project is loaded if Get_View (Project) = GPR.No_Project then return 0; else return Project.Data.Files'Length; end if; end Direct_Sources_Count; ------------------------------ -- Source_File_Data_To_Info -- ------------------------------ function Source_File_Data_To_Info (S : Source_File_Data) return File_Info is Unit : Name_Id := No_Name; begin if S.Source /= null and then S.Source.Unit /= null then Unit := S.Source.Unit.Name; end if; return File_Info' (Project => S.Project, Root_Project => S.Project, File => S.File, Part => Kind_To_Part (S.Source), Name => Unit, Lang => S.Lang); end Source_File_Data_To_Info; ------------------------- -- Create_From_Project -- ------------------------- function Create_From_Project (Self : Project_Type'Class; Name : GNATCOLL.VFS.Filesystem_String) return File_Info is function Find_From_Base_Name (Name : GNATCOLL.VFS.Filesystem_String) return File_Info; -- Find the File_Info from the given base name function Create_From_Full_Name (File : Virtual_File) return File_Info; -- Create File_Info from the given file ------------------------- -- Find_From_Base_Name -- ------------------------- function Find_From_Base_Name (Name : GNATCOLL.VFS.Filesystem_String) return File_Info is Curs : Names_Files.Cursor; File : Virtual_File; Source : Source_File_Data; Imports : Boolean; Iter : Project_Iterator; begin -- Amongst all the files with the right basename, search the one, if -- any, that is visible from Self. Curs := Self.Data.Tree_For_Map.Sources.Find (Name); if Has_Element (Curs) then -- Check amongst all possibilities which one is in Self or its -- imported projects. Source := Element (Curs); loop Imports := Source.Project = Project_Type (Self) or else Source.Project = No_Project; -- predefined source file if not Imports then Iter := Self.Start (Recursive => True, Include_Extended => True); loop exit when Current (Iter) = No_Project; if Current (Iter) = Source.Project then Imports := True; exit; end if; Next (Iter); end loop; end if; if Imports then return Source_File_Data_To_Info (Source); end if; exit when Source.Next = null; Source := Source.Next.all; end loop; end if; -- Search in the predefined source path if Self.Data.Tree.Env.Predefined_Source_Path /= null then File := Locate_Regular_File (Name, Self.Data.Tree.Env.Predefined_Source_Path.all); if File /= GNATCOLL.VFS.No_File then Include_File (Self.Data.Tree_For_Map.Sources, Name, Source_File_Data' (Project => No_Project, File => File, Lang => No_Name, Source => null, Next => null)); return File_Info' (File => File, Project => No_Project, Root_Project => Project_Type (Self), Part => Unit_Separate, Name => No_Name, Lang => No_Name); end if; end if; return (File => GNATCOLL.VFS.No_File, Project => No_Project, Root_Project => Project_Type (Self), Part => Unit_Separate, Name => GPR.No_Name, Lang => GPR.No_Name); end Find_From_Base_Name; --------------------------- -- Create_From_Full_Name -- --------------------------- function Create_From_Full_Name (File : Virtual_File) return File_Info is Result : File_Info; begin Result := Info (Tree => Self.Data.Tree, File => File); if Result.File = GNATCOLL.VFS.No_File then Result := File_Info' (File => File, Project => No_Project, Root_Project => Project_Type (Self), Part => Unit_Separate, Name => GPR.No_Name, Lang => GPR.No_Name); end if; return Result; end Create_From_Full_Name; File : Virtual_File; Result : File_Info; begin if Project_Type (Self) = No_Project then return File_Info' (File => Create (Name), Project => No_Project, Root_Project => Project_Type (Self), Part => Unit_Separate, Name => GPR.No_Name, Lang => GPR.No_Name); end if; if Is_Absolute_Path (Name) then File := Create (Normalize_Pathname (Name, Resolve_Links => False)); return Create_From_Full_Name (File); else -- This is not an absolute name: first check the cache if Self.Data.Base_Name_To_Full_Path = null then -- If it's the first time we need the cache, create it here Self.Data.Base_Name_To_Full_Path := new Basename_To_Info_Cache.Map; end if; if Self.Data.Base_Name_To_Full_Path.Contains (String (Name)) then return Create_From_Full_Name (Self.Data.Base_Name_To_Full_Path.Element (String (Name))); else -- Not found in cache: get the result Result := Find_From_Base_Name (Name); -- .. and add it to the cache Self.Data.Base_Name_To_Full_Path.Insert (String (Name), Result.File); return Result; end if; end if; end Create_From_Project; ------------ -- Create -- ------------ function Create (Self : Project_Tree; Name : Filesystem_String; Project : Project_Type'Class := No_Project; Use_Source_Path : Boolean := True; Use_Object_Path : Boolean := True) return GNATCOLL.VFS.Virtual_File is File : GNATCOLL.VFS.Virtual_File; Ambiguous : Boolean; begin Create (Self, Name, Project, Use_Source_Path, Use_Object_Path, Ambiguous, File); return File; end Create; ------------ -- Create -- ------------ procedure Create (Self : Project_Tree; Name : GNATCOLL.VFS.Filesystem_String; Project : Project_Type'Class := No_Project; Use_Source_Path : Boolean := True; Use_Object_Path : Boolean := True; Ambiguous : out Boolean; File : out GNATCOLL.VFS.Virtual_File; Predefined_Only : Boolean := False) is Tree_For_Map : Project_Tree_Data_Access; -- The root tree Base : constant Filesystem_String := Base_Name (Name); Project2 : Project_Type; Path : Virtual_File := GNATCOLL.VFS.No_File; Iterator : Project_Iterator; Info_Cursor : Names_Files.Cursor; Source_Info : Source_File_Data; In_Predefined : Boolean := False; Duplicate_Obj : Boolean := False; function Ambiguous_Base_Name (First_SFD : Source_File_Data) return Boolean; -- Return false if any of source files in given list has different full -- paths than First_SFD. function Ambiguous_Base_Name (First_SFD : Source_File_Data) return Boolean is Next_SFD : Source_File_Data_Access := First_SFD.Next; begin while Next_SFD /= null loop if Next_SFD.File /= First_SFD.File then return True; end if; Next_SFD := Next_SFD.Next; end loop; return False; end Ambiguous_Base_Name; begin Ambiguous := False; if Self.Data = null then -- No view computed, we do not even know the source dirs File := GNATCOLL.VFS.No_File; return; end if; Tree_For_Map := Self.Data.Root.Data.Tree_For_Map; if Is_Absolute_Path (Name) then File := Create (Normalize_Pathname (Name, Resolve_Links => False)); return; end if; -- Is the file already in the cache ? -- This cache is automatically filled initially when the project is -- loaded, so we know that all source files of the project are in the -- cache and will be returned efficiently if not Predefined_Only and then Project.Data = null and then Use_Source_Path then Info_Cursor := Tree_For_Map.Sources.Find (Base); if Has_Element (Info_Cursor) then -- Multiple cases for ambiguity: -- 1 - multiple possible full paths -- 2 - same full path in multiple projects declare C : Source_File_Data renames Element (Info_Cursor); begin if Ambiguous_Base_Name (Element (Info_Cursor)) then Ambiguous := True; File := GNATCOLL.VFS.No_File; return; end if; if C.Next /= null then Ambiguous := True; end if; end; File := Element (Info_Cursor).File; return; end if; end if; -- When looking for a project file, check among those that are loaded. -- This means we might be looking outside of the source and obj dirs. if Equal (File_Extension (Name), Project_File_Extension) then if Project.Data /= null then Iterator := Project.Start (Recursive => False); else Iterator := Self.Root_Project.Start (Recursive => True); end if; loop Project2 := Current (Iterator); exit when Project2 = No_Project; if Case_Insensitive_Equal (+Project2.Project_Path.Base_Name, +Base) then if Path = GNATCOLL.VFS.No_File then Path := Project2.Project_Path; else -- Duplicate project base name. File := GNATCOLL.VFS.No_File; return; end if; end if; Next (Iterator); end loop; end if; if Path /= GNATCOLL.VFS.No_File then -- Found single project with given base name. File := Path; return; end if; -- We have to search in one or more projects if not Predefined_Only then if Project.Data /= null then Iterator := Project.Start (Recursive => False); else Iterator := Self.Root_Project.Start (Recursive => True); end if; while Path = GNATCOLL.VFS.No_File or else Duplicate_Obj loop -- Checking whenever we have an ambiguous object file. Project2 := Current (Iterator); exit when Project2 = No_Project; if Duplicate_Obj and then Locate_Regular_File (Name, Project2.Object_Path (Recursive => False, Including_Libraries => True)) /= GNATCOLL.VFS.No_File then File := GNATCOLL.VFS.No_File; return; end if; if not Duplicate_Obj and then Use_Source_Path then -- No need to check for object duplicates in source dirs. Path := Locate_Regular_File (Name, Project2.Source_Dirs (Recursive => False)); end if; if Use_Object_Path and then not Duplicate_Obj and then Path = GNATCOLL.VFS.No_File then -- We do not want to loose Path in the check fails. Path := Locate_Regular_File (Name, Project2.Object_Path (Recursive => False, Including_Libraries => True)); if Path /= GNATCOLL.VFS.No_File and then Is_Aggregate_Project (Self.Root_Project) and then Project.Data = null then -- Check is only relevant when root project is aggregate and -- no project has been given as an argument. Duplicate_Obj := True; end if; end if; Next (Iterator); end loop; end if; -- Only search in the predefined directories if the user did not -- specify an explicit project if Path = GNATCOLL.VFS.No_File and then Project.Data = null then if Use_Source_Path and then Self.Data.Env.Predefined_Source_Path /= null then Project2 := No_Project; Path := Locate_Regular_File (Name, Self.Data.Env.Predefined_Source_Path.all); end if; if Use_Object_Path and then Path = GNATCOLL.VFS.No_File and then Self.Data.Env.Predefined_Object_Path /= null then Project2 := No_Project; Path := Locate_Regular_File (Name, Self.Data.Env.Predefined_Object_Path.all); end if; In_Predefined := Path /= GNATCOLL.VFS.No_File; end if; -- If still not found, search in the current directory if Path = GNATCOLL.VFS.No_File then Project2 := No_Project; In_Predefined := False; Path := Locate_Regular_File (Name, (1 => Get_Current_Dir)); end if; -- If found, cache the result for future usage. -- We do not cache anything if the project was forced, however -- since this wouldn't work with extended projects were sources -- can be duplicated. -- Do not cache either files found in the current directory, since that -- could change. -- -- ??? There is a potential issue if for instance we found the file in -- a source dir but the next call specifies Use_Source_Path=>False. But -- that's an unlikely scenario because the user knows where to expect a -- file in general. if Path /= GNATCOLL.VFS.No_File and then Project.Data = null and then (Project2 /= No_Project -- found in a specific project or else In_Predefined) -- or in the runtime -- Make sure the predefined file does not hide a project source -- (since we bypassed the cached above when Predefined_Only is true) and then (not Predefined_Only or else not Tree_For_Map.Sources.Contains (Base)) then -- Language and Source are always unknown: if we had a source file, -- it would have been set in the cache while loading the project. -- However, for runtime files we do compute the language since these -- are likely to be source files Source_Info := Source_File_Data' (Project => No_Project, -- file is not a source File => Path, Lang => No_Name, Source => null, Next => null); if In_Predefined then Source_Info.Lang := Get_String (Language (Info (Self.Data, Path))); end if; Include_File (Tree_For_Map.Sources, Base, Source_Info); end if; File := Path; end Create; ---------- -- Free -- ---------- procedure Free (Self : in out File_And_Project_Array_Access) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (File_And_Project_Array, File_And_Project_Array_Access); begin Unchecked_Free (Self); end Free; ------------------ -- Source_Files -- ------------------ function Source_Files (Project : Project_Type; Recursive : Boolean := False; Include_Project_Files : Boolean := False) return File_And_Project_Array_Access is Count : Natural := 0; Index : Natural; P : Project_Type; Result : File_And_Project_Array_Access; Iter : Project_Iterator := Start (Project, Recursive => Recursive); begin -- Count files loop P := Current (Iter); exit when P = No_Project; if P.Data.Files /= null then Count := Count + P.Data.Files'Length; end if; if Include_Project_Files then Count := Count + 1; end if; Next (Iter); end loop; Result := new File_And_Project_Array (1 .. Count); Index := Result'First; Iter := Start (Project, Recursive => Recursive); loop P := Current (Iter); exit when P = No_Project; if Include_Project_Files then Result (Index) := (File => P.Project_Path, Project => P); Index := Index + 1; end if; if P.Data.Files /= null then for S in P.Data.Files'Range loop Result (Index) := (File => P.Data.Files (S), Project => P); Index := Index + 1; end loop; end if; Next (Iter); end loop; return Result; end Source_Files; ------------------ -- Source_Files -- ------------------ function Source_Files (Project : Project_Type; Recursive : Boolean := False; Include_Externally_Built : Boolean := True) return GNATCOLL.VFS.File_Array_Access is Count : Natural := 0; Index : Natural := 1; P : Project_Type; Sources : File_Array_Access; begin if not Recursive then if Project.Data = null or else Project.Data.Files = null or else (not Include_Externally_Built and then Externally_Built (Project)) then return new File_Array (1 .. 0); else return new File_Array'(Project.Data.Files.all); end if; end if; declare Iter : Project_Iterator := Start (Project, Recursive); begin -- Count files loop P := Current (Iter); exit when P = No_Project; -- Files may be null in case of a parse error if P.Data.Files /= null and then (Include_Externally_Built or else not Externally_Built (P)) then Count := Count + P.Data.Files'Length; end if; Next (Iter); end loop; Sources := new File_Array (1 .. Count); Iter := Start (Project, Recursive); -- Now add files to the Sources array loop P := Current (Iter); exit when P = No_Project; if P.Data.Files /= null and then (Include_Externally_Built or else not Externally_Built (P)) then for S in P.Data.Files'Range loop Sources (Index) := P.Data.Files (S); Index := Index + 1; end loop; end if; Next (Iter); end loop; Sort (Sources.all); return Sources; end; end Source_Files; --------------- -- Unit_Part -- --------------- function Unit_Part (Info : File_Info'Class) return Unit_Parts is begin return Info.Part; end Unit_Part; --------------- -- Unit_Name -- --------------- function Unit_Name (Info : File_Info'Class) return String is begin if Info.Name = No_Name then return ""; else return Get_String (Info.Name); end if; end Unit_Name; -------------- -- Language -- -------------- function Language (Info : File_Info'Class) return String is begin if Info.Lang = No_Name then -- This is likely a file from the predefined search path, for which -- no project information is available. Most likely from the Ada -- runtime. -- ??? Should we return "ada" return ""; else return Get_String (Info.Lang); end if; end Language; ------------- -- Project -- ------------- function Project (Info : File_Info'Class; Root_If_Not_Found : Boolean := False) return Project_Type is begin if Root_If_Not_Found and then Info.Project = No_Project then return Info.Root_Project; else return Info.Project; end if; end Project; ---------- -- Info -- ---------- function Info (Tree : Project_Tree_Data_Access; File : GNATCOLL.VFS.Virtual_File) return File_Info is Part : Unit_Parts; Id : Source_Id; Full : String := String (File.Full_Name (Normalize => True, Resolve_Links => not Tree.Env.Trusted_Mode).all); Path : Path_Name_Type; Lang : Name_Id; begin if File = GNATCOLL.VFS.No_File then return (File => GNATCOLL.VFS.No_File, Project => No_Project, Root_Project => Tree.Root, Part => Unit_Separate, Name => GPR.No_Name, Lang => GPR.No_Name); end if; -- Lookup in the project's Source_Paths_HT, rather than in -- Registry.Data.Sources, since the latter does not support duplicate -- base names. In GPR.Nmsc, names have been converted to lower case on -- case-insensitive file systems, so we need to do the same here. -- (Insertion is done in Check_File, where the Path passed in parameter -- comes from a call to Normalize_Pathname with the following args: -- Resolve_Links => Opt.Follow_Links_For_Files -- Case_Sensitive => True -- So we use the normalized name in the above call to Full_Name for -- full compatibility between GPS and the project manager Osint.Canonical_Case_File_Name (Full); Path := Path_Name_Type (Name_Id'(Get_String (Full))); Id := Source_Paths_Htable.Get (Tree.View.Source_Paths_HT, Path); if Id /= No_Source then Part := Kind_To_Part (Id); if Id.Unit /= null then return File_Info' (Project => Project_Type (Project_From_Name (Tree, Id.Project.Name)), Root_Project => Tree.Root, File => File, Part => Part, Name => Id.Unit.Name, Lang => Id.Language.Name); else return File_Info' (Project => Project_Type (Project_From_Name (Tree, Id.Project.Name)), Root_Project => Tree.Root, File => File, Part => Part, Name => No_Name, Lang => Id.Language.Name); end if; end if; -- Either the file was not cached, or there is no Source info. In both -- cases, that means the file is not a source file (although it might be -- a predefined source file), so we just use the default naming scheme. declare Ext : constant Filesystem_String := File.File_Extension (Normalize => True); Cursor : Extensions_Languages.Cursor; NS : Naming_Scheme_Access; begin if Ext = ".ads" then -- Do not compute the unit names, which requires parsing the file -- or the ALI file, since the GNAT runtime uses krunched names return File_Info' (Project => No_Project, Root_Project => Tree.Root, File => File, Part => Unit_Spec, Name => GPR.No_Name, Lang => Name_Ada); elsif Ext = ".adb" then return File_Info' (Project => No_Project, Root_Project => Tree.Root, File => File, Part => Unit_Body, Name => GPR.No_Name, Lang => Name_Ada); end if; -- Try and guess the language from the registered extensions if Ext = "" then -- This is a file without extension like Makefile or -- ChangeLog for example. Use the filename to get the proper -- language for this file. Cursor := Tree.Env.Extensions.Find (Base_Name (Full)); else Cursor := Tree.Env.Extensions.Find (+Ext); end if; if Has_Element (Cursor) then Lang := Extensions_Languages.Element (Cursor); else Lang := GPR.No_Name; NS := Tree.Env.Naming_Schemes; while NS /= null loop if +Ext = NS.Default_Spec_Suffix.all or else +Ext = NS.Default_Body_Suffix.all then Lang := Get_String (NS.Language.all); exit; end if; NS := NS.Next; end loop; end if; end; return (File => GNATCOLL.VFS.No_File, Project => No_Project, Root_Project => Tree.Root, Part => Unit_Separate, Name => GPR.No_Name, Lang => Lang); end Info; ---------- -- Info -- ---------- function Info (Self : Project_Tree'Class; File : GNATCOLL.VFS.Virtual_File) return File_Info is begin if Self.Data = null then raise Program_Error with "no project tree was parsed"; end if; if Is_Aggregate_Project (Self.Data.Root) then raise Program_Error with "root project is aggregate, cannot use Info"; end if; return Info (Self.Data, File); end Info; -------------- -- Info_Set -- -------------- function Info_Set (Self : Project_Tree'Class; File : GNATCOLL.VFS.Virtual_File) return File_Info_Set is M_Cur : Names_Files.Cursor; B_Name : constant Filesystem_String := File.Base_Name; Source : Source_File_Data; S_Info : File_Info; Tree_For_Map : Project_Tree_Data_Access; Result : File_Info_Set := (File_Info_Sets.Empty_Set with null record); function Unit_Kind_To_Part (Src_Kind : GPR.Source_Kind) return Unit_Parts; -- Translate GPR.Source_Kind into Unit_Parts. function Unit_Kind_To_Part (Src_Kind : GPR.Source_Kind) return Unit_Parts is begin case Src_Kind is when Spec => return Unit_Spec; when Impl => return Unit_Body; when Sep => return Unit_Separate; end case; end Unit_Kind_To_Part; begin if Self.Data = null then raise Program_Error with "no project tree was parsed"; end if; Tree_For_Map := Self.Data.Root.Data.Tree_For_Map; M_Cur := Tree_For_Map.Sources.Find (B_Name); if M_Cur = Names_Files.No_Element then Result.Include (Info (Self.Data, File)); return Result; end if; Source := Names_Files.Element (M_Cur); loop if Source.File = File then S_Info.Project := Source.Project; S_Info.Root_Project := Self.Root_Project; S_Info.File := File; if Source.Source /= null then -- One of the initially cached source files. S_Info.Part := Unit_Kind_To_Part (Source.Source.Kind); if Source.Source.Unit = No_Unit_Index then -- Not applicable to C and other non unit-based languages. S_Info.Name := No_Name; else S_Info.Name := Source.Source.Unit.Name; end if; S_Info.Lang := Source.Source.Language.Name; else -- Cached after a call to create, thus no Source_Id. The only -- thing known is the language. declare Tmp_Info : constant File_Info := Info (Self.Data, File); begin S_Info.Part := Tmp_Info.Part; S_Info.Lang := Tmp_Info.Lang; S_Info.Name := Tmp_Info.Name; end; end if; Result.Include (S_Info); end if; exit when Source.Next = null; Source := Source.Next.all; end loop; if Result.Is_Empty then -- The file does not belong to the project. However, we can still -- make some guesses regarding its language and various pieces of -- information. Result.Include (Info (Self.Data, File)); end if; return Result; end Info_Set; ---------- -- File -- ---------- function File (Info : File_Info'Class) return GNATCOLL.VFS.Virtual_File is begin return Info.File; end File; -------------------- -- Substitute_Dot -- -------------------- function Substitute_Dot (Unit_Name : String; Dot_Replacement : String) return String is Dot_Count : Natural := 0; begin for U in Unit_Name'Range loop if Unit_Name (U) = '.' then Dot_Count := Dot_Count + 1; end if; end loop; declare Uname : String (1 .. Unit_Name'Length + Dot_Count * (Dot_Replacement'Length - 1)); Index : Natural := Uname'First; begin for U in Unit_Name'Range loop if Unit_Name (U) = '.' then Uname (Index .. Index + Dot_Replacement'Length - 1) := Dot_Replacement; Index := Index + Dot_Replacement'Length; else Uname (Index) := Unit_Name (U); Index := Index + 1; end if; end loop; return Uname; end; end Substitute_Dot; -------------------- -- File_From_Unit -- -------------------- function File_From_Unit (Project : Project_Type; Unit_Name : String; Part : Unit_Parts; Language : String; File_Must_Exist : Boolean := True) return Filesystem_String is function Has_Predefined_Prefix (S : String) return Boolean; -- Return True is S has a name that starts like a predefined unit -- (e.g. a.b, which should be replaced by a~b) --------------------------- -- Has_Predefined_Prefix -- --------------------------- function Has_Predefined_Prefix (S : String) return Boolean is C : constant Character := S (S'First); begin return S (S'First + 1) = '-' and then (C = 'a' or else C = 'g' or else C = 'i' or else C = 's'); end Has_Predefined_Prefix; Unit : Name_Id; UIndex : Unit_Index; Lang : Language_Ptr; begin if Is_Ada_Predefined_Unit (Unit_Name) then declare Buffer : String := Substitute_Dot (Unit_Name, "-"); Len : Natural := Buffer'Length; begin pragma Assert (Buffer'First = 1); GNATCOLL.Projects.Krunch.Krunch (Buffer, Len, Maxlen => Buffer'Length, No_Predef => False); case Part is when Unit_Body | Unit_Separate => return +Buffer (1 .. Len) & ".adb"; when Unit_Spec => return +Buffer (1 .. Len) & ".ads"; end case; end; end if; -- Standard GNAT naming scheme -- ??? This isn't language independent, what if other languages have -- similar requirements. Should use configuration files as gprbuild does if Project = No_Project then if Language = "ada" then case Part is when Unit_Body => return +Substitute_Dot (Unit_Name, "-") & ".adb"; when Unit_Spec => return +Substitute_Dot (Unit_Name, "-") & ".ads"; when others => Assert (Me, False, "Unexpected Unit_Part"); return ""; end case; else return ""; end if; -- The project naming scheme else Name_Len := Unit_Name'Length; Name_Buffer (1 .. Name_Len) := To_Lower (Unit_Name); Unit := Name_Find; -- Take advantage of computation done by the project manager when we -- looked for source files UIndex := Units_Htable.Get (Project.Tree_View.Units_HT, Unit); if UIndex /= No_Unit_Index then case Part is when Unit_Body | Unit_Separate => if UIndex.File_Names (Impl) /= null then return +Get_String (UIndex.File_Names (Impl).File); end if; when Unit_Spec => if UIndex.File_Names (Spec) /= null then return +Get_String (UIndex.File_Names (Spec).File); end if; end case; end if; -- The unit does not exist yet. Perhaps we are creating a new file -- and trying to guess the correct file name if File_Must_Exist then return ""; end if; -- We can only perform guesses if the language is a valid for the -- project. Lang := Get_Language_From_Name (Get_View (Project), Language); if Lang = null then return ""; end if; declare Dot_Replacement : constant String := Get_String (Name_Id (Lang.Config.Naming_Data.Dot_Replacement)); Uname : String := Substitute_Dot (Unit_Name, Dot_Replacement); begin case Lang.Config.Naming_Data.Casing is when All_Lower_Case => To_Lower (Uname); when All_Upper_Case => To_Upper (Uname); when others => null; end case; -- Handle properly special naming such as a.b -> a~b if Case_Insensitive_Equal (Language, "ada") and then Uname'Length > 2 and then Has_Predefined_Prefix (Uname) then Uname (Uname'First + 1) := '~'; end if; case Part is when Unit_Body => return +(Uname & Get_Name_String (Name_Id (Lang.Config.Naming_Data.Body_Suffix))); when Unit_Spec => return +(Uname & Get_Name_String (Name_Id (Lang.Config.Naming_Data.Spec_Suffix))); when Unit_Separate => return +(Uname & Get_Name_String (Name_Id (Lang.Config.Naming_Data.Separate_Suffix))); end case; end; end if; end File_From_Unit; ---------------- -- Other_File -- ---------------- function Other_File (Self : Project_Tree; File : GNATCOLL.VFS.Virtual_File) return GNATCOLL.VFS.Virtual_File is -- Should we ask the user for the project ? -- in practice, it is likely that the other file is in the same -- project, so whichever project tree we choose we would likely end up -- with the same other file. Info : constant File_Info := File_Info (Self.Info_Set (File).First_Element); Unit : constant String := Unit_Name (Info); Part : Unit_Parts; function Test_Suffixes (Old_Suffix, New_Suffix : String) return Virtual_File; -- Substitute prefixes and check whether the file exists function Non_Unit_Based (Old_Part, New_Part : Attribute_Pkg_String) return Virtual_File; -- Handling of non-unit based languages ------------------- -- Test_Suffixes -- ------------------- function Test_Suffixes (Old_Suffix, New_Suffix : String) return Virtual_File is Other_F : constant Virtual_File := Self.Create (File.Base_Name (+Old_Suffix) & (+New_Suffix), Use_Object_Path => False); begin if Other_F = GNATCOLL.VFS.No_File then return File; else return Other_F; end if; end Test_Suffixes; -------------------- -- Non_Unit_Based -- -------------------- function Non_Unit_Based (Old_Part, New_Part : Attribute_Pkg_String) return Virtual_File is Suffix : constant String := Info.Project.Attribute_Value (Old_Part, Index => Info.Language); New_Suffix : constant String := Info.Project.Attribute_Value (New_Part, Index => Info.Language); begin return Test_Suffixes (Suffix, New_Suffix); end Non_Unit_Based; begin case Info.Part is when Unit_Spec => Part := Unit_Body; when Unit_Body | Unit_Separate => Part := Unit_Spec; end case; -- Do we have a unit-based language ? if Unit /= "" then -- Is there such a file in the project ? declare Base : constant Filesystem_String := File_From_Unit (Project (Info), Unit, Part, Language => Info.Language); begin if Base'Length > 0 then return Self.Create (Base, Use_Object_Path => False); end if; end; -- Special case for separate units, since the spec is a parent -- package if Info.Part = Unit_Separate then for J in reverse Unit'Range loop if Unit (J) = '.' then declare Base : constant Filesystem_String := File_From_Unit (Project (Info), Unit (Unit'First .. J - 1), Part, Language => Info.Language); begin if Base'Length > 0 then return Self.Create (Base, Use_Object_Path => False); end if; end; end if; end loop; end if; -- Else try to guess from naming scheme declare Base : constant Filesystem_String := File_From_Unit (Project (Info), Unit, Part, Language => Info.Language, File_Must_Exist => False); begin if Base'Length > 0 then return GNATCOLL.VFS.Create_From_Dir (Dir => Create (Dir_Name (File)), Base_Name => Base); end if; end; -- Else try the default GNAT naming scheme for runtime files if Case_Insensitive_Equal (Info.Language, "ada") then declare Base : constant Filesystem_String := File_From_Unit (Project (Info), Unit, Part, Language => Info.Language); begin if Base'Length > 0 then return Self.Create (Base, Use_Object_Path => False); end if; end; end if; end if; -- Else simply try switching the extensions (useful for krunched names) -- for unit-based languages. -- For non-unit based languages, we only guess the "other file" if it -- actually exists in the project. We never try to create one, since -- there is no insurance the user needs one or its name will be -- consistent. if Info.Project = No_Project then case Info.Part is when Unit_Spec => return Test_Suffixes (".ads", ".adb"); when Unit_Body | Unit_Separate => return Test_Suffixes (".adb", ".ads"); end case; else case Info.Part is when Unit_Spec => return Non_Unit_Based (Spec_Suffix_Attribute, Impl_Suffix_Attribute); when Unit_Body | Unit_Separate => return Non_Unit_Based (Impl_Suffix_Attribute, Spec_Suffix_Attribute); end case; end if; end Other_File; --------------------- -- Attribute_Value -- --------------------- function Attribute_Value (Project : Project_Type; Attribute : String; Index : String := ""; Use_Extended : Boolean := False) return Variable_Value is Sep : constant Natural := Ada.Strings.Fixed.Index (Attribute, "#"); Attribute_Name : constant String := String (Attribute (Sep + 1 .. Attribute'Last)); Pkg_Name : constant String := String (Attribute (Attribute'First .. Sep - 1)); Project_View : constant Project_Id := Get_View (Project); Pkg : Package_Id := No_Package; Value : Variable_Value := Nil_Variable_Value; Var : Variable_Id; Arr : Array_Id; Elem : Array_Element_Id; N : Name_Id; Shared : Shared_Project_Tree_Data_Access; begin if Project_View = GPR.No_Project then return Nil_Variable_Value; end if; Shared := Project.Tree_View.Shared; if Pkg_Name /= "" then Pkg := Value_Of (Get_String (Pkg_Name), In_Packages => Project_View.Decl.Packages, Shared => Shared); if Pkg = No_Package then if Use_Extended and then Extended_Project (Project) /= No_Project then return Attribute_Value (Extended_Project (Project), Attribute, Index, Use_Extended); else return Nil_Variable_Value; end if; end if; Var := Shared.Packages.Table (Pkg).Decl.Attributes; Arr := Shared.Packages.Table (Pkg).Decl.Arrays; else Var := Project_View.Decl.Attributes; Arr := Project_View.Decl.Arrays; end if; N := Get_String (Attribute_Name); if Index /= "" then Elem := Value_Of (N, In_Arrays => Arr, Shared => Shared); if Elem /= No_Array_Element then Value := Value_Of (Index => Get_String (Index), In_Array => Elem, Shared => Shared); end if; else Value := Value_Of (N, In_Variables => Var, Shared => Shared); end if; if Value.Location = No_Location and then Use_Extended and then Extended_Project (Project) /= No_Project then return Attribute_Value (Extended_Project (Project), Attribute, Index, Use_Extended); else return Value; end if; end Attribute_Value; --------------------- -- Attribute_Value -- --------------------- function Attribute_Value (Project : Project_Type; Attribute : Attribute_Pkg_String; Index : String := ""; Default : String := ""; Use_Extended : Boolean := False) return String is View : constant Project_Id := Get_View (Project); Value : Variable_Value; Lang : Language_Ptr; Unit : Unit_Index; begin if Project = No_Project or else View = GPR.No_Project then return Default; end if; -- Special case for the naming scheme, since we need to get access to -- the default registered values for foreign languages if Attribute = Spec_Suffix_Attribute or else Attribute = Specification_Suffix_Attribute then Lang := Get_Language_From_Name (View, Index); if Lang /= null then return Get_String (Lang.Config.Naming_Data.Spec_Suffix); else declare Default : constant String := Default_Spec_Suffix (Project.Data.Tree.Env.all, Index); begin if Default = Dummy_Suffix then return ""; else return Default; end if; end; end if; elsif Attribute = Impl_Suffix_Attribute or else Attribute = Implementation_Suffix_Attribute then Lang := Get_Language_From_Name (View, Index); if Lang /= null then return Get_String (Lang.Config.Naming_Data.Body_Suffix); else declare Default : constant String := Default_Body_Suffix (Project.Data.Tree.Env.all, Index); begin if Default = Dummy_Suffix then return ""; else return Default; end if; end; end if; elsif Attribute = Separate_Suffix_Attribute then Lang := Get_Language_From_Name (View, "ada"); if Lang /= null then return Get_String (Lang.Config.Naming_Data.Separate_Suffix); else return ""; end if; elsif Attribute = Casing_Attribute then Lang := Get_Language_From_Name (View, "ada"); if Lang /= null then return GPR.Image (Lang.Config.Naming_Data.Casing); else return ""; end if; elsif Attribute = Dot_Replacement_Attribute then Lang := Get_Language_From_Name (View, "ada"); if Lang /= null then return Get_String (Lang.Config.Naming_Data.Dot_Replacement); else return ""; end if; elsif Attribute = Old_Implementation_Attribute or else Attribute = Body_Attribute then -- Index is a unit name Unit := Units_Htable.Get (Project.Tree_View.Units_HT, Get_String (Index)); if Unit /= No_Unit_Index and then Unit.File_Names (Impl) /= null then if Unit.File_Names (Impl).Index /= 0 then return Get_String (Unit.File_Names (Impl).Display_File) & " at" & Unit.File_Names (Impl).Index'Img; else return Get_String (Unit.File_Names (Impl).Display_File); end if; else -- We might have a separate or some other value. Fallback to -- looking in the attribute itself (but this won't handle the -- Index part -- perhaps separates are not usable in a multi-unit -- source file, which would seem logical anyway) null; end if; elsif Attribute = Old_Specification_Attribute or else Attribute = Spec_Attribute then -- Index is a unit name Unit := Units_Htable.Get (Project.Tree_View.Units_HT, Get_String (Index)); if Unit /= No_Unit_Index and then Unit.File_Names (Spec) /= null then if Unit.File_Names (Spec).Index /= 0 then return Get_String (Unit.File_Names (Spec).Display_File) & " at" & Unit.File_Names (Spec).Index'Img; else return Get_String (Unit.File_Names (Spec).Display_File); end if; else return ""; end if; end if; Value := Attribute_Value (Project, String (Attribute), Index, Use_Extended); case Value.Kind is when Undefined => return Default; when Single => return Value_Of (Value, Default); when List => Trace (Me, "Attribute " & String (Attribute) & " is not a single string"); return Default; end case; end Attribute_Value; ----------------------- -- Attribute_Project -- ----------------------- function Attribute_Project (Project : Project_Type; Attribute : Attribute_Pkg_String; Index : String := "") return Project_Type is Value : constant Variable_Value := Attribute_Value (Project, String (Attribute), Index); Tree : constant Project_Tree := (Data => Project.Data.Tree); begin if Value.Project = GPR.No_Project then return No_Project; else declare Name : constant String := Get_Name_String (Value.Project.Name); begin return Tree.Project_From_Name (Name); end; end if; end Attribute_Project; -------------------------- -- Attribute_Registered -- -------------------------- function Attribute_Registered (Name : String; Pkg : String) return Boolean is Lower_Pkg : constant String := To_Lower (Pkg); Pkg_Id : Package_Node_Id := Empty_Package; begin -- Need to make sure the predefined packages are already declared, or -- the new one will be discarded. GPR.Attr.Initialize; if Lower_Pkg = "" then Trace (Me, "Attribute_Registered called for empty package"); return True; end if; Pkg_Id := Package_Node_Id_Of (Get_String (Lower_Pkg)); if Pkg_Id = Empty_Package or else Pkg_Id = Unknown_Package then -- We don't even have such a package. return False; end if; return GPR.Attr.Attribute_Registered (Name, Pkg_Id); end Attribute_Registered; ---------------------------- -- Variable_Value_To_List -- ---------------------------- function Variable_Value_To_List (Project : Project_Type; Value : Variable_Value) return GNAT.Strings.String_List_Access is V : String_List_Id; S : String_List_Access; Shared : Shared_Project_Tree_Data_Access; begin case Value.Kind is when Undefined => return null; when Single => -- ??? Should we really convert to a list return new String_List' (1 .. 1 => new String'(Get_Name_String (Value.Value))); when List => S := new String_List (1 .. Length (Project.Tree_View, Value.Values)); V := Value.Values; Shared := Project.Tree_View.Shared; for J in S'Range loop Get_Name_String (Shared.String_Elements.Table (V).Value); S (J) := new String'(Name_Buffer (1 .. Name_Len)); V := Shared.String_Elements.Table (V).Next; end loop; return S; end case; end Variable_Value_To_List; --------------------- -- Attribute_Value -- --------------------- function Attribute_Value (Project : Project_Type; Attribute : Attribute_Pkg_List; Index : String := ""; Use_Extended : Boolean := False) return GNAT.Strings.String_List_Access is Value : constant Variable_Value := Attribute_Value (Project, String (Attribute), Index, Use_Extended); begin return Variable_Value_To_List (Project, Value); end Attribute_Value; ------------------- -- Has_Attribute -- ------------------- function Has_Attribute (Project : Project_Type; Attribute : String; Index : String := "") return Boolean is Shared : Shared_Project_Tree_Data_Access; Sep : constant Natural := Ada.Strings.Fixed.Index (Attribute, "#"); Attribute_Name : constant String := String (Attribute (Sep + 1 .. Attribute'Last)); Pkg_Name : constant String := String (Attribute (Attribute'First .. Sep - 1)); Project_View : constant Project_Id := Get_View (Project); Pkg : Package_Id := No_Package; Var : Variable_Id; Arr : Array_Id; N, I : Name_Id; Arr_Elem_Id : Array_Element_Id; begin if Project_View = GPR.No_Project then return False; end if; Shared := Project.Tree_View.Shared; if Pkg_Name /= "" then Pkg := Value_Of (Get_String (Pkg_Name), In_Packages => Project_View.Decl.Packages, Shared => Shared); if Pkg = No_Package then Trace (Me, "No such package " & Pkg_Name); return False; end if; Var := Shared.Packages.Table (Pkg).Decl.Attributes; Arr := Shared.Packages.Table (Pkg).Decl.Arrays; else Var := Project_View.Decl.Attributes; Arr := Project_View.Decl.Arrays; end if; N := Get_String (Attribute_Name); if Index /= "" then -- ??? That seems incorrect, we are not testing for the specific -- index Arr_Elem_Id := Value_Of (N, In_Arrays => Arr, Shared => Shared); if Arr_Elem_Id = No_Array_Element then return False; end if; I := Get_String (Index); return Value_Of (I, In_Array => Arr_Elem_Id, Shared => Shared) /= Nil_Variable_Value; else return not Value_Of (N, Var, Shared).Default; end if; end Has_Attribute; function Has_Attribute (Project : Project_Type; Attribute : Attribute_Pkg_String; Index : String := "") return Boolean is begin return Has_Attribute (Project, String (Attribute), Index); end Has_Attribute; function Has_Attribute (Project : Project_Type; Attribute : Attribute_Pkg_List; Index : String := "") return Boolean is begin return Has_Attribute (Project, String (Attribute), Index); end Has_Attribute; ----------------------- -- Attribute_Indexes -- ----------------------- function Attribute_Indexes (Project : Project_Type; Attribute : String; Use_Extended : Boolean := False) return GNAT.Strings.String_List is Shared : Shared_Project_Tree_Data_Access; Sep : constant Natural := Ada.Strings.Fixed.Index (Attribute, "#"); Attribute_Name : constant String := String (Attribute (Sep + 1 .. Attribute'Last)); Pkg_Name : constant String := String (Attribute (Attribute'First .. Sep - 1)); Project_View : constant Project_Id := Get_View (Project); Packages : GPR.Package_Table.Table_Ptr; Array_Elements : GPR.Array_Element_Table.Table_Ptr; Pkg : Package_Id := No_Package; Arr : Array_Id; Elem, Elem2 : Array_Element_Id; N : Name_Id; Count : Natural := 0; begin if Project_View = GPR.No_Project then return (1 .. 0 => null); end if; Shared := Project.Tree_View.Shared; Packages := Shared.Packages.Table; Array_Elements := Shared.Array_Elements.Table; if Pkg_Name /= "" then Pkg := Value_Of (Get_String (Pkg_Name), In_Packages => Project_View.Decl.Packages, Shared => Shared); if Pkg = No_Package then if Use_Extended and then Extended_Project (Project) /= No_Project then return Attribute_Indexes (Extended_Project (Project), Attribute, Use_Extended); else return (1 .. 0 => null); end if; end if; Arr := Packages (Pkg).Decl.Arrays; else Arr := Project_View.Decl.Arrays; end if; N := Get_String (Attribute_Name); Elem := Value_Of (N, In_Arrays => Arr, Shared => Shared); if Elem = No_Array_Element and then Use_Extended and then Extended_Project (Project) /= No_Project then return Attribute_Indexes (Extended_Project (Project), Attribute, Use_Extended); end if; Elem2 := Elem; while Elem2 /= No_Array_Element loop Count := Count + 1; Elem2 := Array_Elements (Elem2).Next; end loop; declare Result : String_List (1 .. Count); begin Count := Result'First; while Elem /= No_Array_Element loop Result (Count) := new String' (Get_String (Array_Elements (Elem).Index)); Count := Count + 1; Elem := Array_Elements (Elem).Next; end loop; return Result; end; end Attribute_Indexes; function Attribute_Indexes (Project : Project_Type; Attribute : Attribute_Pkg_String; Use_Extended : Boolean := False) return GNAT.Strings.String_List is begin return Attribute_Indexes (Project, String (Attribute), Use_Extended); end Attribute_Indexes; function Attribute_Indexes (Project : Project_Type; Attribute : Attribute_Pkg_List; Use_Extended : Boolean := False) return GNAT.Strings.String_List is begin return Attribute_Indexes (Project, String (Attribute), Use_Extended); end Attribute_Indexes; --------------- -- Languages -- --------------- function Languages (Project : Project_Type; Recursive : Boolean := False) return String_List is Iter : Inner_Project_Iterator := Start (Project, Recursive); Num_Languages : Natural := 0; Val : Variable_Value; P : Project_Type; procedure Add_Language (Lang : in out String_List; Index : in out Natural; Str : String); -- Add a new language in the list, if not already there ------------------ -- Add_Language -- ------------------ procedure Add_Language (Lang : in out String_List; Index : in out Natural; Str : String) is Normalized : String := Str; Idx : constant Integer := Normalized'First; begin To_Lower (Normalized); Normalized (Idx) := GNAT.Case_Util.To_Upper (Normalized (Idx)); for L in Lang'First .. Index - 1 loop if Lang (L).all = Normalized then return; end if; end loop; Lang (Index) := new String'(Normalized); Index := Index + 1; end Add_Language; begin if Get_View (Project) = GPR.No_Project then return GNAT.OS_Lib.Argument_List'(1 .. 1 => new String'("ada")); end if; loop P := Current (Iter); exit when P = No_Project; Val := Attribute_Value (P, String (Languages_Attribute)); case Val.Kind is when Undefined => null; when Single => Num_Languages := Num_Languages + 1; when List => Num_Languages := Num_Languages + Length (P.Tree_View, Val.Values); end case; Next (Iter); end loop; Iter := Start (Project, Recursive); declare -- If no project defines the language attribute, then they have -- Ada as an implicit language. Save space for it. Lang : Argument_List (1 .. Num_Languages + 1); Index : Natural := Lang'First; Value : String_List_Id; begin loop P := Current (Iter); exit when P = No_Project; if not P.Has_Attribute (Languages_Attribute) then Add_Language (Lang, Index, "ada"); else Val := Attribute_Value (P, String (Languages_Attribute)); case Val.Kind is when Undefined => null; when Single => Add_Language (Lang, Index, Get_Name_String (Val.Value)); when List => Value := Val.Values; while Value /= Nil_String loop Add_Language (Lang, Index, Get_String (String_Elements (P.Data.Tree)(Value).Value)); Value := String_Elements (P.Data.Tree)(Value).Next; end loop; end case; end if; Next (Iter); end loop; return Lang (Lang'First .. Index - 1); end; end Languages; ------------------ -- Has_Language -- ------------------ function Has_Language (Project : Project_Type; Language : String) return Boolean is Normalized_Lang : constant Name_Id := Get_String (To_Lower (Language)); P : constant Project_Id := Get_View (Project); Lang : Language_Ptr; begin if P /= GPR.No_Project then Lang := P.Languages; while Lang /= null loop if Lang.Name = Normalized_Lang then return True; end if; Lang := Lang.Next; end loop; end if; return False; end Has_Language; ------------------------------- -- Get_Automatic_Config_File -- ------------------------------- function Get_Automatic_Config_File (Self : Project_Environment) return Boolean is begin return Self.Autoconf; end Get_Automatic_Config_File; ------------------ -- Get_Closures -- ------------------ procedure Get_Closures (Project : Project_Type; Mains : GNATCOLL.VFS.File_Array_Access; All_Projects : Boolean := True; Include_Externally_Built : Boolean := False; Status : out Status_Type; Result : out GNATCOLL.VFS.File_Array_Access) is Mains_Str_List : String_Vectors.Vector; Closure_Status : GPR.Util.Status_Type; Closures_List : String_Vectors.Vector; begin Trace (Me, "Get_Closures"); Unchecked_Free (Result); if Mains = null or else Mains'Length = 0 or else Project = No_Project then Status := Error; return; end if; for I in Mains'Range loop Mains_Str_List.Append (Mains (I).Display_Base_Name); end loop; GPR.Util.Get_Closures (Project.Get_View, Project.Tree_View, Mains => Mains_Str_List, All_Projects => All_Projects, Include_Externally_Built => Include_Externally_Built, Status => Closure_Status, Result => Closures_List); case Closure_Status is when Success => Status := Success; when Incomplete_Closure => Status := Incomplete_Closure; when others => Trace (Me, "cannot get closure, " & GPR.Util.Status_Type'Image (Closure_Status)); Status := Error; return; end case; if Closure_Status in Success | Incomplete_Closure then for Closure of Closures_List loop Append (Result, Create (+Closure)); end loop; end if; end Get_Closures; --------------------- -- Get_Config_File -- --------------------- function Get_Config_File (Self : Project_Environment) return GNATCOLL.VFS.Virtual_File is begin return Self.Config_File; end Get_Config_File; ---------------- -- Get_Target -- ---------------- function Get_Target (Project : Project_Type; Default_To_Host : Boolean := True) return String is Prj : Project_Type := Project; Target_From_Attribute : constant String := Project.Attribute_Value (Attribute => Target_Attribute, Use_Extended => True); function Extract_From_Attribute (Attribute : Attribute_Pkg_String; Suffix : String) return String; -- Attempt to extract target from the value of the given attribute, -- assuming the value is of the form . ---------------------------- -- Extract_From_Attribute -- ---------------------------- function Extract_From_Attribute (Attribute : Attribute_Pkg_String; Suffix : String) return String is Val : constant String := Project.Attribute_Value (Attribute => Attribute, Use_Extended => True); SL : constant Natural := Suffix'Length; begin if Val'Length > Suffix'Length and then To_Lower (Val (Val'Last - SL + 1 .. Val'Last)) = Suffix then return Val (Val'First .. Val'Last - SL); end if; return ""; end Extract_From_Attribute; begin -- What this explicitly set in the environment ? if Project.Data.Tree.Env.Forced_Target /= null then return Project.Data.Tree.Env.Forced_Target.all; end if; -- First check whether the "Target" attribute is explicitly given if Target_From_Attribute /= "" then -- The attribute target is defined and non-empty: look no further! -- But we need to clarify where does this attribute come from. -- It may be either declared in the project itself or in one of -- projects extending it, or it may be inherited from cgpr. -- In the last case we do not want to return it. while Prj /= No_Project loop declare Target_Value : constant Variable_Value := Value_Of (Get_String ("target"), Prj.Data.View.Decl.Attributes, Prj.Data.Tree.View.Shared); begin if Target_Value.Project = Prj.Data.View then return Target_From_Attribute; end if; end; Prj := Extended_Project (Prj); end loop; end if; -- Next: look for the legacy way of defining the target via -- the "gnat" in the package "ide". We expect something of the form -- "arm-eabi-gnat"; -- and we assume the target is the first part. declare G : constant String := Extract_From_Attribute (GNAT_Attribute, "-gnat"); begin if G /= "" then return G; end if; end; -- Also look, similarly, at the gnatls attribute, expecting something -- of the form "arm-eabi-gnatls" declare G : constant String := Extract_From_Attribute (Gnatlist_Attribute, "-gnatls"); begin if G /= "" then return G; end if; end; -- Nothing? The target is not defined. if Default_To_Host then return Target_From_Attribute; else return ""; end if; end Get_Target; ----------------- -- Get_Runtime -- ----------------- function Get_Runtime (Project : Project_Type) return String is List : GNAT.Strings.String_List_Access; S : String_Access; begin -- What this explicitly set in the environment ? if Project.Data.Tree.Env.Forced_Runtime /= null then return Project.Data.Tree.Env.Forced_Runtime.all; end if; -- First check whether the "Runtime" attribute is explicitly given declare Runtime : constant String := Project.Attribute_Value (Attribute => Runtime_Attribute, Index => "ada", Use_Extended => True); begin if Runtime /= "" then -- Got it! return Runtime; end if; end; -- Look for the legacy way of specifying the runtime as a --RTS -- argument in the builder switches. List := Project.Attribute_Value (Attribute => Builder_Default_Switches_Attribute, Index => "ada", Use_Extended => True); if List /= null then for L in List'Range loop S := List (L); if S /= null and then S'Length > 5 and then To_Lower (S (S'First .. S'First + 5)) = "--rts=" then return S (S'First + 6 .. S'Last); end if; end loop; end if; -- No runtime defined return ""; end Get_Runtime; ------------------------- -- Target_Same_As_Host -- ------------------------- function Target_Same_As_Host (Project : Project_Type) return Boolean is Tgt : constant String := Normalize_Target_Name (Project.Get_Target); begin if Tgt = "" then return True; end if; for T of Host_Targets_List loop if T = Tgt then return True; end if; end loop; return False; end Target_Same_As_Host; ------------------ -- Is_Main_File -- ------------------ function Is_Main_File (Project : Project_Type; File : GNATCOLL.VFS.Filesystem_String; Case_Sensitive : Boolean := True) return Boolean is Value : String_List_Access := Project.Attribute_Value (Attribute => Main_Attribute, Use_Extended => True); B_File : constant GNATCOLL.VFS.Filesystem_String := Base_Name (File); Files : VFS.File_Array_Access; Source : Boolean := False; begin Trace (Me, (+File) & " vs " & (+B_File)); if GNATCOLL.VFS_Utils.Is_Absolute_Path (File) then -- Check that given file is a source of Project first. Files := Project.Source_Files (Recursive => False); for F of Files.all loop if F.Full_Name = File then Source := True; exit; end if; end loop; Unchecked_Free (Files); if not Source then Free (Value); return False; end if; end if; for V in Value'Range loop if Equal (Value (V).all, +B_File, Case_Sensitive => Case_Sensitive) then Free (Value); return True; end if; end loop; Free (Value); return False; end Is_Main_File; ------------------- -- Get_Directory -- ------------------- function Get_Directory (Project : Project_Type; Callback : Get_Directory_Path_Callback) return Virtual_File is begin if Project = No_Project or else Get_View (Project) = GPR.No_Project then return GNATCOLL.VFS.No_File; else declare Dir : constant Filesystem_String := +Get_String (Name_Id (Callback (Get_View (Project)).Display_Name)); begin if Dir'Length > 0 then return Create (Name_As_Directory (Dir)); else -- ??? Can't we simply access Object_Dir in the view ? declare Path : constant File_Array := Project.Object_Path; begin if Path'Length /= 0 then return Path (Path'First); else return GNATCOLL.VFS.No_File; end if; end; end if; end; end if; end Get_Directory; --------------------------- -- Executables_Directory -- --------------------------- function Executables_Directory (Project : Project_Type) return Virtual_File is function Get_Exec_Directory_Callback (Project : GPR.Project_Id) return Path_Information; ---------------------------------- -- Get_Exec_Directory_Callback -- ---------------------------------- function Get_Exec_Directory_Callback (Project : GPR.Project_Id) return Path_Information is begin return Project.Exec_Directory; end Get_Exec_Directory_Callback; begin return Get_Directory (Project, Get_Exec_Directory_Callback'Unrestricted_Access); end Executables_Directory; ----------------------- -- Library_Directory -- ----------------------- function Library_Directory (Project : Project_Type) return GNATCOLL.VFS.Virtual_File is function Get_Library_Dir_Callback (Project : GPR.Project_Id) return Path_Information; ------------------------------ -- Get_Library_Dir_Callback -- ------------------------------ function Get_Library_Dir_Callback (Project : GPR.Project_Id) return Path_Information is begin return Project.Library_Dir; end Get_Library_Dir_Callback; begin return Get_Directory (Project, Get_Library_Dir_Callback'Unrestricted_Access); end Library_Directory; --------------------------- -- Library_Ali_Directory -- --------------------------- function Library_Ali_Directory (Project : Project_Type) return GNATCOLL.VFS.Virtual_File is function Get_Library_ALI_Dir_Callback (Project : GPR.Project_Id) return Path_Information; ---------------------------------- -- Get_Library_ALI_Dir_Callback -- ---------------------------------- function Get_Library_ALI_Dir_Callback (Project : GPR.Project_Id) return Path_Information is begin return Project.Library_ALI_Dir; end Get_Library_ALI_Dir_Callback; begin return Get_Directory (Project, Get_Library_ALI_Dir_Callback'Unrestricted_Access); end Library_Ali_Directory; --------------------------- -- For_Each_Project_Node -- --------------------------- procedure For_Each_Project_Node (Tree : GPR.Project_Node_Tree_Ref; Root : Project_Node_Id; Callback : access procedure (Tree : GPR.Project_Node_Tree_Ref; Node : Project_Node_Id)) is use Project_Sets; Seen : Project_Sets.Set; procedure Process_Project (Proj : Project_Node_Id); --------------------- -- Process_Project -- --------------------- procedure Process_Project (Proj : Project_Node_Id) is With_Clause : Project_Node_Id := First_With_Clause_Of (Proj, Tree); Extended : Project_Node_Id; begin if not Seen.Contains (Proj) then Seen.Include (Proj); Callback (Tree, Proj); while With_Clause /= Empty_Project_Node loop -- We have to ignore links back to the root project, -- which could only happen with "limited with", since -- otherwise the root project would not appear first in -- the topological sort, and then Start returns invalid -- results at least when its Recursive parameters is set -- to False. if Project_Node_Of (With_Clause, Tree) /= Root and then not Is_Virtual_Extending (Tree, Project_Node_Of (With_Clause, Tree)) then Process_Project (Project_Node_Of (With_Clause, Tree)); end if; With_Clause := Next_With_Clause_Of (With_Clause, Tree); end loop; -- Is this an extending project ? Extended := Extended_Project_Of (Project_Declaration_Of (Proj, Tree), Tree); if Extended /= Empty_Project_Node then Process_Project (Extended); end if; end if; end Process_Project; begin Process_Project (Root); end For_Each_Project_Node; ------------------------------- -- Compute_Imported_Projects -- ------------------------------- procedure Compute_Imported_Projects (Project : Project_Type'Class) is begin if Project.Data /= null and then Project.Data.Imported_Projects.Items = null then declare procedure Do_Add (T : GPR.Project_Node_Tree_Ref; P : Project_Node_Id); procedure Do_Add (T : GPR.Project_Node_Tree_Ref; P : Project_Node_Id) is Path : constant Path_Name_Type := GPR.Tree.Path_Name_Of (P, T); begin Append (Project.Data.Imported_Projects, Path); end Do_Add; begin For_Each_Project_Node (Project.Data.Tree.Tree, Project.Data.Node, Do_Add'Unrestricted_Access); end; end if; end Compute_Imported_Projects; -------------------- -- Start_Reversed -- -------------------- function Start_Reversed (Root_Project : Project_Type; Recursive : Boolean := True; Direct_Only : Boolean := False; Include_Extended : Boolean := True) return Project_Iterator is Iter : Project_Iterator; Project_Paths : Path_Sets.Set; procedure Add_Project (Project : Project_Type'Class); -- Fills Project_Iterator with a list of projects. For each of -- aggregated project trees (if any) corresponding projects are put in -- the list in the same reversed topological order as for regular -- project. Aggregate project itself goes in front of corresponding -- aggregated projects. procedure Add_Project (Project : Project_Type'Class) is P : Project_Type; Aggregated : Aggregated_Project_List; Iter_Inner : Inner_Project_Iterator; begin if Project.Get_View = GPR.No_Project then -- View has not been computed for this project. return; end if; if Is_Aggregate_Project (Project) then -- processing aggregated project hierarchies Aggregated := Project.Data.View.Aggregated_Projects; -- aggregate project goes first in reversed order. if Project_Paths.Find (Project_Path (Project).Display_Full_Name) = Path_Sets.No_Element then Iter.Project_List.Append (Project_Type (Project)); Project_Paths.Include (Project_Path (Project).Display_Full_Name); end if; while Aggregated /= null loop P := Project_Type (Project_From_Path (Project.Data.Tree, Aggregated.Path)); if Direct_Only then if Project_Paths.Find (Project_Path (P).Display_Full_Name) = Path_Sets.No_Element then -- we only need projects that are not yet in the list Iter.Project_List.Append (P); Project_Paths.Include (Project_Path (P).Display_Full_Name); end if; else Add_Project (P); end if; Aggregated := Aggregated.Next; end loop; end if; -- For the regular project (aggregated or root) do a full -- iteration placing projects in the list. Iter_Inner := Start_Reversed (Root_Project => Project, Recursive => Recursive, Direct_Only => Direct_Only, Include_Extended => Include_Extended); loop exit when Current (Iter_Inner) = No_Project; if Project_Paths.Find (Current (Iter_Inner).Project_Path.Display_Full_Name) = Path_Sets.No_Element then -- we only need projects that are not yet in the list if Is_Aggregate_Project (Current (Iter_Inner)) and then not Direct_Only then Add_Project (Current (Iter_Inner)); else Iter.Project_List.Append (Current (Iter_Inner)); Project_Paths.Include (Current (Iter_Inner).Project_Path.Display_Full_Name); end if; end if; Next (Iter_Inner); end loop; end Add_Project; begin Iter.Root := Root_Project; if not Recursive then Iter.Project_List.Append (Root_Project); Iter.Project_Idx := Iter.Project_List.First_Index; return Iter; end if; Add_Project (Root_Project); Project_Paths.Clear; Iter.Project_Idx := Iter.Project_List.First_Index; return Iter; end Start_Reversed; -------------------- -- Start_Reversed -- -------------------- function Start_Reversed (Root_Project : Project_Type; Recursive : Boolean := True; Direct_Only : Boolean := False; Include_Extended : Boolean := True) return Inner_Project_Iterator is Iter : Inner_Project_Iterator; begin Assert (Me, Root_Project.Data /= null, "Start: Uninitialized project passed as argument"); Compute_Imported_Projects (Root_Project); if Recursive then Iter := Inner_Project_Iterator' (Root => Root_Project, Direct_Only => Direct_Only, Importing => False, Reversed => True, Include_Extended => Include_Extended, Current => Root_Project.Data.Imported_Projects.Items'First - 1); Next (Iter); return Iter; else -- Include_Extended is in fact ignored here, since we only ever -- return the root project. return Inner_Project_Iterator' (Root => Root_Project, Direct_Only => Direct_Only, Importing => False, Reversed => False, -- irrelevant Include_Extended => Include_Extended, Current => Root_Project.Data.Imported_Projects.Items'First); end if; end Start_Reversed; ----------- -- Start -- ----------- function Start (Root_Project : Project_Type; Recursive : Boolean := True; Direct_Only : Boolean := False; Include_Extended : Boolean := True) return Project_Iterator is Iter : Project_Iterator; Project_Paths : Path_Sets.Set; procedure Add_Project (Project : Project_Type'Class); -- Fills Project_Iterator with a list of projects. For each of -- aggregated project trees (if any) corresponding projects are put in -- the list in the same topological order as for regular project. -- Aggregate project itself goes after corresponding aggregated -- projects. procedure Add_Project (Project : Project_Type'Class) is P : Project_Type; Aggregated : Aggregated_Project_List; Iter_Inner : Inner_Project_Iterator; begin if Project.Get_View = GPR.No_Project then -- View has not been computed for this project. return; end if; if Is_Aggregate_Project (Project) then -- processing aggregated project hierarchies Aggregated := Project.Data.View.Aggregated_Projects; while Aggregated /= null loop P := Project_Type (Project_From_Path (Project.Data.Tree_For_Map, Aggregated.Path)); if not Project_Paths.Contains (P.Project_Path.Display_Full_Name) then if Direct_Only then Project_Paths.Include (P.Project_Path.Display_Full_Name); Iter.Project_List.Append (P); else Add_Project (P); end if; end if; Aggregated := Aggregated.Next; end loop; -- aggregate project goes last in straight order if not Project_Paths.Contains (Project.Project_Path.Display_Full_Name) then Project_Paths.Include (Project.Project_Path.Display_Full_Name); Iter.Project_List.Append (Project_Type (Project)); end if; end if; -- For the regular project (aggregated or root) do a full -- iteration placing projects in the list. Iter_Inner := Start (Root_Project => Project, Recursive => Recursive, Direct_Only => Direct_Only, Include_Extended => Include_Extended); loop P := Current (Iter_Inner); exit when P = No_Project; if not Project_Paths.Contains (P.Project_Path.Display_Full_Name) then Project_Paths.Include (P.Project_Path.Display_Full_Name); if Is_Aggregate_Project (P) and then not Direct_Only then -- aggregate library Add_Project (P); else Iter.Project_List.Append (P); end if; end if; Next (Iter_Inner); end loop; end Add_Project; begin Iter.Root := Root_Project; if not Recursive then Iter.Project_List.Append (Root_Project); Iter.Project_Idx := Iter.Project_List.First_Index; return Iter; end if; Add_Project (Root_Project); Project_Paths.Clear; Iter.Project_Idx := Iter.Project_List.First_Index; return Iter; end Start; ----------- -- Start -- ----------- function Start (Root_Project : Project_Type; Recursive : Boolean := True; Direct_Only : Boolean := False; Include_Extended : Boolean := True) return Inner_Project_Iterator is Iter : Inner_Project_Iterator; begin Compute_Imported_Projects (Root_Project); if Recursive then Iter := Inner_Project_Iterator' (Root => Root_Project, Direct_Only => Direct_Only, Importing => False, Reversed => False, Include_Extended => Include_Extended, Current => Root_Project.Data.Imported_Projects.Last + 1); Next (Iter); return Iter; else return Inner_Project_Iterator' (Root => Root_Project, Direct_Only => Direct_Only, Importing => False, Reversed => False, -- irrelevant Include_Extended => Include_Extended, Current => Root_Project.Data.Imported_Projects.Items'First); end if; end Start; --------------------- -- Project_Imports -- --------------------- procedure Project_Imports (Parent : Project_Type; Child : Project_Type'Class; Include_Extended : Boolean := False; Imports : out Boolean; Is_Limited_With : out Boolean) is With_Clause : Project_Node_Id; Extended : Project_Node_Id; T : constant GPR.Project_Node_Tree_Ref := Parent.Data.Tree.Tree; begin Assert (Me, Child.Data /= null, "Project_Imports: no child provided"); if Parent = No_Project then Imports := True; Is_Limited_With := False; return; end if; With_Clause := First_With_Clause_Of (Parent.Data.Node, T); while With_Clause /= Empty_Project_Node loop -- We cannot compare the nodes directly, since they might be the same -- in two aggregated projects, even when this is not the same project if Get_Name_String (Path_Name_Of (Project_Node_Of (With_Clause, T), T)) = Child.Project_Path.Display_Full_Name then Imports := True; Is_Limited_With := Non_Limited_Project_Node_Of (With_Clause, T) = Empty_Project_Node; return; end if; With_Clause := Next_With_Clause_Of (With_Clause, T); end loop; -- Handling for extending projects ? if Include_Extended then Extended := Extended_Project_Of (Project_Declaration_Of (Parent.Data.Node, T), T); if Extended = Child.Data.Node then Imports := True; Is_Limited_With := False; return; end if; end if; -- Handling aggregate libraries if Is_Aggregate_Library (Parent) then Is_Limited_With := False; declare Aggregated : Aggregated_Project_List := Parent.Data.View.Aggregated_Projects; P : Project_Type; begin while Aggregated /= null loop P := Project_Type (Project_From_Path (Parent.Data.Tree, Aggregated.Path)); if P.Data = Child.Data then Imports := True; return; end if; Aggregated := Aggregated.Next; end loop; end; end if; Imports := False; Is_Limited_With := False; end Project_Imports; -------------------------------- -- Compute_Importing_Projects -- -------------------------------- procedure Compute_Importing_Projects (Project : Project_Type'Class; Root_Project : Project_Type'Class) is type Boolean_Array is array (Positive range <>) of Boolean; All_Prj : Path_Name_Id_Array_Access := Root_Project.Data.Imported_Projects.Items; All_Prj_Last : Integer := Root_Project.Data.Imported_Projects.Last; Importing : Path_Name_Id_Array_Access; Index : Integer; Parent : Project_Type; Imports, Is_Limited_With : Boolean; procedure Merge_Project (P : Project_Type; Inc : in out Boolean_Array); -- Merge the imported projects of P with the ones for Project ------------------- -- Merge_Project -- ------------------- procedure Merge_Project (P : Project_Type; Inc : in out Boolean_Array) is Index2 : Integer; begin for J in P.Data.Importing_Projects'Range loop Index2 := All_Prj'First; while All_Prj (Index2) /= P.Data.Importing_Projects (J) loop Index2 := Index2 + 1; end loop; Inc (Index2) := True; end loop; end Merge_Project; begin if Project.Data.Importing_Projects /= null then return; end if; -- Prevent a recursive call to this procedure: if the project has -- a "limited with", we could end up calling Compute_Importing_Project -- again for the same project, thus an infinite loop. To prevent this, -- we set Dummy. That means however that we will not correctly compute -- the list of imported project for imported projects below, so we -- should not store them. Project.Data.Importing_Projects := Unknown_Importing_Projects'Unrestricted_Access; if All_Prj = null then Compute_Imported_Projects (Root_Project); All_Prj := Root_Project.Data.Imported_Projects.Items; All_Prj_Last := Root_Project.Data.Imported_Projects.Last; end if; -- We consider that an extending project is "importing" its -- extended project, since it relies on it. declare Include : Boolean_Array (1 .. All_Prj_Last) := (others => False); Was_Unknown : Boolean; begin for Index in Include'Range loop Parent := Project_Type (Project_From_Path (Project.Data.Tree, All_Prj (Index))); -- Avoid processing a project twice if not Include (Index) and then Parent /= Project_Type (Project) then Project_Imports (Parent, Child => Project, Include_Extended => True, Imports => Imports, Is_Limited_With => Is_Limited_With); if Imports then Include (Index) := True; -- The list computed for Parent might be incorrect is -- somewhere there is a "limited with" that goes back to -- Project (since we have set a Dummy above to prevent -- infinite recursion). So we will reset the list to -- null below, which means we might end up recomputing -- it later. Was_Unknown := Parent.Data.Importing_Projects = null or else Parent.Data.Importing_Projects.all'Address = Unknown_Importing_Projects'Address; Compute_Importing_Projects (Parent, Root_Project); Merge_Project (Parent, Include); if Was_Unknown then -- We cannot rely on the computed value if the parent -- was also importing Project, so we must reset the cache -- in that case. Otherwise keep the cache for maximum -- efficiency for J in Parent.Data.Importing_Projects'Range loop if Parent.Data.Importing_Projects (J) = Get_View (Project).Path.Name then Unchecked_Free (Parent.Data.Importing_Projects); exit; end if; end loop; end if; end if; end if; end loop; -- Done processing everything Index := 0; for Inc in Include'Range loop if Include (Inc) then Index := Index + 1; end if; end loop; -- Keep the last place for the project itself Importing := new Path_Name_Id_Array (1 .. Index + 1); Index := Importing'First; for Inc in Include'Range loop if Include (Inc) then Importing (Index) := All_Prj (Inc); Index := Index + 1; end if; end loop; end; Importing (Importing'Last) := GPR.Tree.Path_Name_Of (Project.Data.Node, Project.Data.Tree.Tree); Project.Data.Importing_Projects := Importing; -- The code below is used for debugging if Active (Debug) then Trace (Debug, "Find_All_Projects_Importing: " & Project.Name); for J in Project.Data.Importing_Projects'Range loop Trace (Debug, Get_String (Project.Data.Importing_Projects (J))); end loop; end if; exception when E : others => Trace (Me, E); if Project.Data.Importing_Projects.all'Address /= Unknown_Importing_Projects'Address then Unchecked_Free (Project.Data.Importing_Projects); end if; Project.Data.Importing_Projects := null; end Compute_Importing_Projects; --------------------------------- -- Find_All_Projects_Importing -- --------------------------------- function Find_All_Projects_Importing (Project : Project_Type; Include_Self : Boolean := False; Direct_Only : Boolean := False) return Project_Iterator is Iter, Cleanup_Iter : Project_Iterator; Iter_Inner : Inner_Project_Iterator; Local_Roots : Project_Lists.Vector := Project_Lists.Empty_Vector; Project_Paths : Path_Sets.Set := Path_Sets.Empty_Set; procedure Add_Local_Roots (Project : Project_Type); -- creating a list of root level aggregated projects procedure Add_Local_Roots (Project : Project_Type) is P : Project_Type; Aggregated : Aggregated_Project_List; begin if Is_Aggregate_Project (Project) then Aggregated := Project.Data.View.Aggregated_Projects; while Aggregated /= null loop P := Project_Type (Project_From_Path (Project.Data.Tree, Aggregated.Path)); Add_Local_Roots (P); Aggregated := Aggregated.Next; end loop; else Local_Roots.Append (Project); end if; end Add_Local_Roots; begin Iter.Root := Project; Iter.Importing := True; if Is_Aggregate_Project (Project.Data.Tree_For_Map.Root) then -- We need to look for importing projects in all trees created for -- each directly aggregated project. Add_Local_Roots (Project.Data.Tree_For_Map.Root); for I in Local_Roots.First_Index .. Local_Roots.Last_Index loop Iter_Inner := Find_All_Projects_Importing (Project => Project, Root_Project => Local_Roots.Element (I), Include_Self => Include_Self, Direct_Only => Direct_Only); loop exit when Current (Iter_Inner) = No_Project; if not Project_Paths.Contains (Current (Iter_Inner).Project_Path.Display_Full_Name) then -- avoiding possible duplication Iter.Project_List.Append (Current (Iter_Inner)); Project_Paths.Include (Current (Iter_Inner).Project_Path.Display_Full_Name); end if; Next (Iter_Inner); end loop; -- We need to reset importing projects for each local root -- and the project in question before the next pass. Unchecked_Free (Project.Data.Importing_Projects); Cleanup_Iter := Start (Local_Roots (I)); while Current (Cleanup_Iter) /= No_Project loop Unchecked_Free (Current (Cleanup_Iter).Data.Importing_Projects); Next (Cleanup_Iter); end loop; end loop; Iter.Project_Idx := Iter.Project_List.First_Index; end if; Iter_Inner := Find_All_Projects_Importing (Project => Project, Root_Project => Project.Data.Tree_For_Map.Root, Include_Self => Include_Self, Direct_Only => Direct_Only); loop exit when Current (Iter_Inner) = No_Project; if not Project_Paths.Contains (Current (Iter_Inner).Project_Path.Display_Full_Name) then Project_Paths.Include (Current (Iter_Inner).Project_Path.Display_Full_Name); Iter.Project_List.Append (Current (Iter_Inner)); end if; Next (Iter_Inner); end loop; -- Again, we need to clean up all stored Importing_Projects, otherwise -- if somewhere in the hierarchy there is an aggregate/aggregate library -- project, the stored info is not correct. Cleanup_Iter := Start (Project.Data.Tree_For_Map.Root); while Current (Cleanup_Iter) /= No_Project loop Unchecked_Free (Current (Cleanup_Iter).Data.Importing_Projects); Next (Cleanup_Iter); end loop; Iter.Project_Idx := Iter.Project_List.First_Index; Project_Paths.Clear; return Iter; end Find_All_Projects_Importing; --------------------------------- -- Find_All_Projects_Importing -- --------------------------------- function Find_All_Projects_Importing (Project : Project_Type; Root_Project : Project_Type; Include_Self : Boolean := False; Direct_Only : Boolean := False) return Inner_Project_Iterator is Iter : Inner_Project_Iterator; begin if Project = No_Project then return Start (Root_Project, Recursive => True); end if; Trace (Me, "Find_All_Projects_Importing " & Project.Name & " with root=" & Root_Project.Name); Compute_Imported_Projects (Root_Project); Compute_Importing_Projects (Project, Root_Project); Iter := Inner_Project_Iterator' (Root => Project, Direct_Only => Direct_Only, Importing => True, Reversed => False, Include_Extended => True, -- ??? Should this be configurable Current => Project.Data.Importing_Projects'Last + 1); -- The project itself is always at index 'Last if not Include_Self then Iter.Current := Iter.Current - 1; end if; Next (Iter); return Iter; end Find_All_Projects_Importing; ------------- -- Current -- ------------- function Current (Iterator : Project_Iterator) return Project_Type is begin if Iterator.Project_List.To_Cursor (Iterator.Project_Idx) = Project_Lists.No_Element then return No_Project; end if; return Iterator.Project_List.Element (Iterator.Project_Idx); end Current; ------------- -- Current -- ------------- function Current (Iterator : Inner_Project_Iterator) return Project_Type is P : Path_Name_Type; begin if Iterator.Importing then if Iterator.Current >= Iterator.Root.Data.Importing_Projects'First then return Project_Type (Project_From_Path (Iterator.Root.Data.Tree_For_Map, Iterator.Root.Data.Importing_Projects (Iterator.Current))); end if; elsif Iterator.Current >= Iterator.Root.Data.Imported_Projects.Items'First and then Iterator.Current <= Iterator.Root.Data.Imported_Projects.Last then P := Iterator.Root.Data.Imported_Projects.Items (Iterator.Current); return Project_Type (Project_From_Path (Iterator.Root.Data.Tree_For_Map, P)); end if; return No_Project; end Current; --------------------- -- Is_Limited_With -- --------------------- function Is_Limited_With (Iterator : Project_Iterator) return Boolean is Imports, Is_Limited_With : Boolean; begin if Iterator.Importing then if Is_Aggregate_Project (Iterator.Root) then -- aggregate projects cannot be imported return False; end if; Project_Imports (Current (Iterator), Iterator.Root, Include_Extended => False, Imports => Imports, Is_Limited_With => Is_Limited_With); else Project_Imports (Iterator.Root, Current (Iterator), Include_Extended => False, Imports => Imports, Is_Limited_With => Is_Limited_With); end if; return Imports and Is_Limited_With; end Is_Limited_With; --------------------- -- Is_Limited_With -- --------------------- function Is_Limited_With (Iterator : Inner_Project_Iterator) return Boolean is Imports, Is_Limited_With : Boolean; begin if Iterator.Importing then Project_Imports (Current (Iterator), Iterator.Root, Include_Extended => False, Imports => Imports, Is_Limited_With => Is_Limited_With); else Project_Imports (Iterator.Root, Current (Iterator), Include_Extended => False, Imports => Imports, Is_Limited_With => Is_Limited_With); end if; return Imports and Is_Limited_With; end Is_Limited_With; ---------- -- Next -- ---------- procedure Next (Iterator : in out Project_Iterator) is begin Iterator.Project_Idx := Iterator.Project_Idx + 1; end Next; ---------- -- Next -- ---------- procedure Next (Iterator : in out Inner_Project_Iterator) is Imports, Is_Limited_With : Boolean; begin if Iterator.Reversed then Iterator.Current := Iterator.Current + 1; if Iterator.Direct_Only then if Iterator.Importing then while Iterator.Current <= Iterator.Root.Data.Importing_Projects'Last loop Project_Imports (Current (Iterator), Iterator.Root, Iterator.Include_Extended, Imports => Imports, Is_Limited_With => Is_Limited_With); exit when Imports; Iterator.Current := Iterator.Current + 1; end loop; else while Iterator.Current <= Iterator.Root.Data.Imported_Projects.Last loop Project_Imports (Iterator.Root, Current (Iterator), Iterator.Include_Extended, Imports => Imports, Is_Limited_With => Is_Limited_With); exit when Imports; Iterator.Current := Iterator.Current + 1; end loop; end if; end if; else Iterator.Current := Iterator.Current - 1; if Iterator.Direct_Only then if Iterator.Importing then while Iterator.Current >= Iterator.Root.Data.Importing_Projects'First loop Project_Imports (Current (Iterator), Iterator.Root, Iterator.Include_Extended, Imports => Imports, Is_Limited_With => Is_Limited_With); exit when Imports; Iterator.Current := Iterator.Current - 1; end loop; else while Iterator.Current >= Iterator.Root.Data.Imported_Projects.Items'First loop Project_Imports (Iterator.Root, Current (Iterator), Iterator.Include_Extended, Imports => Imports, Is_Limited_With => Is_Limited_With); exit when Imports; Iterator.Current := Iterator.Current - 1; end loop; end if; end if; end if; end Next; -------------------------------- -- Compute_Scenario_Variables -- -------------------------------- procedure Compute_Scenario_Variables (Tree : Project_Tree_Data_Access; Recursive : Boolean := True; Errors : Error_Report := null) is Typed_List : Scenario_Variable_Array_Access; Untyped_List : Untyped_Variable_Array_Access; T_Curr : Positive; U_Curr : Positive; T_Curr2 : Natural; Var_Quantity : Natural; package Name_Id_Sets is new Ada.Containers.Ordered_Sets (GPR.Name_Id); Inconsistent_SC_Externals : Name_Id_Sets.Set := Name_Id_Sets.Empty_Set; function Count_Vars return Natural; -- Return the number of scenario variables in tree function Not_Already (UVs : Untyped_Variable_Array_Access; Last : Positive; Ext_Name : GPR.Name_Id) return Boolean; -- Checks that an untyped variable with same name -- has not been registered yet. procedure Register_Var (Variable : Project_Node_Id; Proj : Project_Node_Id; Pkg : Project_Node_Id; Project : Project_Type); -- Wrapper that calls either Register_Scenario_Var or -- Register_Untyped_Var depending on the kind of the variable. procedure Register_Scenario_Var (Variable : Project_Node_Id; Proj : Project_Node_Id; Pkg : Project_Node_Id; Project : Project_Type; Errors : Error_Report := null); -- Add the variable to the list of scenario variables, if not there yet -- (see the documentation for Scenario_Variables for the exact rules -- used to detect aliases). procedure Register_Untyped_Var (Variable : Project_Node_Id; Proj : Project_Node_Id; Pkg : Project_Node_Id; Project : Project_Type); -- Likewise, add the variable to the list of untyped variables. function External_Default (Project : Project_Type; Var : Project_Node_Id; Pkg : Project_Node_Id; T : GPR.Project_Node_Tree_Ref; Nested_Expr : Project_Node_Id := Empty_Project_Node) return Name_Id; -- Return the default value for the variable. Var must be a variable -- declaration or a variable reference. This routine supports only -- all kinds of expressions, but for composite values it will set on -- the Uses_Variables flag for the root project. -- Expr is only used for nested external references in the variable -- declaration to evaluate the proper expression. ---------------- -- Count_Vars -- ---------------- function Count_Vars return Natural is Count : Natural := 0; procedure Cb (Variable : Project_Node_Id; Prj : Project_Node_Id; Pkg : Project_Node_Id; Project : Project_Type); -- Increment the total number of variables -------- -- Cb -- -------- procedure Cb (Variable : Project_Node_Id; Prj : Project_Node_Id; Pkg : Project_Node_Id; Project : Project_Type) is pragma Unreferenced (Prj, Pkg); Node_Tree : constant GPR.Project_Node_Tree_Ref := Project.Data.Tree.Tree; Expr : Project_Node_Id := Expression_Of (Variable, Node_Tree); begin while Expr /= Empty_Project_Node loop Expr := First_Term (Expr, Node_Tree); if Next_Term (Expr, Node_Tree) /= Empty_Project_Node then -- Non-canonical nesting, we do not care. return; end if; Expr := Current_Term (Expr, Node_Tree); if Kind_Of (Expr, Node_Tree) = N_External_Value then Count := Count + 1; -- That is nesting, we need to iterate deeper. Expr := External_Default_Of (Expr, Node_Tree); else -- End of nesting. return; end if; end loop; end Cb; begin For_Each_External_Variable_Declaration (Tree.Root, Recursive => Recursive, Callback => Cb'Unrestricted_Access); return Count; end Count_Vars; ---------------------- -- External_Default -- ---------------------- function External_Default (Project : Project_Type; Var : Project_Node_Id; Pkg : Project_Node_Id; T : GPR.Project_Node_Tree_Ref; Nested_Expr : Project_Node_Id := Empty_Project_Node) return Name_Id is V : Variable_Value; Name : constant String := Get_Name_String (GPR.Tree.Name_Of (Var, T)); -- For diagnostic purposes. Proj : Project_Type := Tree.Root; Expr : Project_Node_Id := (if Nested_Expr = Empty_Project_Node then Expression_Of (Var, T) else Nested_Expr); procedure Check_Complexity (Expression : Project_Node_Id); -- Check whether or not the default value is a simple one, -- and mark project tree not editable, if the value is complex. procedure Check_Complexity (Expression : Project_Node_Id) is Expr : Project_Node_Id := Expression; begin if Kind_Of (Expr, T) /= N_Literal_String then Expr := First_Term (Expr, T); if Next_Term (Expr, T) /= Empty_Project_Node then Trace (Me, "No project editing: " & "Default value cannot be a concatenation"); Proj.Data.Uses_Variables := True; -- Prevent edition return; end if; Expr := Current_Term (Expr, T); if Kind_Of (Expr, T) = N_Variable_Reference then -- A variable reference, look for the corresponding string -- literal. declare Var : constant Name_Id := GPR.Tree.Name_Of (Expr, T); In_Prj : constant Project_Node_Id := Project_Node_Of (Expr, T); Decl : Project_Node_Id; begin if In_Prj /= Empty_Project_Node then -- This variable is defined in another project, get -- project reference. Proj := Project_Type (Project_From_Name (Tree, GPR.Tree.Name_Of (In_Prj, T))); else Proj := Project; end if; -- Look for Var declaration into the project Decl := First_Declarative_Item_Of (Project_Declaration_Of (Proj.Data.Node, T), T); while Decl /= Empty_Project_Node loop Expr := Current_Item_Node (Decl, T); if GPR.Tree.Name_Of (Expr, T) = Var then Expr := Expression_Of (Expr, T); Expr := First_Term (Expr, T); -- Get expression and corresponding term -- Check now that this is not a composite value if Next_Term (Expr, T) /= Empty_Project_Node then Trace (Me, "No project editing: " & "Default value cannot be a concatenation"); Proj.Data.Uses_Variables := True; -- Prevent edition return; end if; -- Get the string literal Expr := Current_Term (Expr, T); exit; end if; Decl := Next_Declarative_Item (Decl, T); end loop; end; end if; if Kind_Of (Expr, T) /= N_Literal_String then Trace (Me, "No project editing: " & "Default value can only be literal string"); Proj.Data.Uses_Variables := True; -- prevent edition return; end if; end if; end Check_Complexity; The_Name : Name_Id := No_Name; The_Package : Package_Id := No_Package; begin Expr := First_Term (Expr, T); Expr := Current_Term (Expr, T); if Kind_Of (Expr, T) /= N_External_Value then return No_Name; end if; Expr := External_Default_Of (Expr, T); if Expr = Empty_Project_Node then return No_Name; end if; Check_Complexity (Expr); The_Name := GPR.Tree.Name_Of (Pkg, T); The_Package := Project.Get_View.Decl.Packages; while The_Package /= No_Package and then Project.Tree_View.Shared.Packages.Table (The_Package).Name /= The_Name loop The_Package := Project.Tree_View.Shared.Packages.Table (The_Package).Next; end loop; if Active (Me_SV) then if Nested_Expr = Empty_Project_Node then Trace (Me_SV, "We will try to compute default of:"); else Trace (Me_SV, "We will try to compute default " & "of a nested sub-expression from:"); end if; Pretty_Print (Var, T, Backward_Compatibility => False); end if; V := GPR.Proc.Expression (Project => Project.Data.View, Shared => Project.Tree_View.Shared, From_Project_Node => Project.Node, From_Project_Node_Tree => T, Env => Project.Data.Tree.Env.Env, Pkg => The_Package, First_Term => First_Term (Expr, T), Kind => Expression_Kind_Of (Expr, T)); Trace (Me_SV, "Value is: " & Get_Name_String (V.Value)); return V.Value; exception when Ex : others => Trace (Me_SV, "Error when computing default for " & Name & " from project " & Project.Name & ":"); Trace (Me_SV, Exception_Information (Ex)); return GPR.No_Name; end External_Default; ----------------- -- Not_Already -- ----------------- function Not_Already (UVs : Untyped_Variable_Array_Access; Last : Positive; Ext_Name : GPR.Name_Id) return Boolean is begin for I in 1 .. Last - 1 loop if UVs (I).Name = Ext_Name then return False; end if; end loop; return True; end Not_Already; ------------------ -- Register_Var -- ------------------ procedure Register_Var (Variable : Project_Node_Id; Proj : Project_Node_Id; Pkg : Project_Node_Id; Project : Project_Type) is T : constant GPR.Project_Node_Tree_Ref := Project.Data.Tree.Tree; function Is_Simple_Scenario_Variable return Boolean; -- Check whether or not given variable is a simple canonical -- Scenario Variable, that is there are no concatenations in the -- default value or after the external variable declaration -- and so on. function Is_Simple_Scenario_Variable return Boolean is Expr : Project_Node_Id; begin Expr := First_Term (Expression_Of (Variable, T), T); if Next_Term (Expr, T) /= Empty_Project_Node then -- Not good, we have a declaration of the following kind: -- Val : Type := External ("Ext", "default") & return False; end if; Expr := Expression_Of (Variable, T); Expr := First_Term (Expr, T); Expr := Current_Term (Expr, T); Expr := External_Default_Of (Expr, T); if Expr /= Empty_Project_Node and then Kind_Of (Expr, T) /= N_Literal_String then Expr := First_Term (Expr, T); if Next_Term (Expr, T) /= Empty_Project_Node then -- Not good, we have a declaration of the following kind: -- Val : Type := External ("Ext", "default" & ) return False; end if; end if; return True; end Is_Simple_Scenario_Variable; begin Trace (Me_SV, "Project: " & Project.Project_Path.Display_Full_Name); case Kind_Of (Variable, T) is when N_Variable_Declaration => Register_Untyped_Var (Variable, Proj, Pkg, Project); when N_Typed_Variable_Declaration => if Is_Simple_Scenario_Variable then Register_Scenario_Var (Variable, Proj, Pkg, Project, Errors); else Register_Untyped_Var (Variable, Proj, Pkg, Project); end if; when others => Trace (Me, "Unexpected kind of variable"); end case; end Register_Var; --------------------------- -- Register_Scenario_Var -- --------------------------- procedure Register_Scenario_Var (Variable : Project_Node_Id; Proj : Project_Node_Id; Pkg : Project_Node_Id; Project : Project_Type; Errors : Error_Report := null) is pragma Unreferenced (Proj, Errors); T : constant GPR.Project_Node_Tree_Ref := Project.Data.Tree.Tree; V : constant Name_Id := External_Reference_Of (Variable, T); N : constant String := Get_String (V); Var : Scenario_Variable; Is_Valid, Duplicate_Found : Boolean; function "<" (L, R : String_Access) return Boolean is (L.all < R.all); procedure Sort_Values is new Ada.Containers.Generic_Array_Sort (Positive, String_Access, String_List); procedure Look_For_Duplicate_SVs (Ext_Ref_Name : String; Found : out Boolean); -- Compare current Var with all already stored Scenario Variables -- and if found check that they have same set of possible values. procedure Look_For_Duplicate_SVs (Ext_Ref_Name : String; Found : out Boolean) is Old_Var : Scenario_Variable := No_Variable; Dummy : Project_Tree; -- Possible_Values_Of doesn't reference the Tree parameter -- that has been left only for compatibility. begin for Index in 1 .. T_Curr - 1 loop if External_Name (Typed_List (Index)) = Ext_Ref_Name then Trace (Me_SV, "Same external already registered," & " comparing set of possible values"); Old_Var := Typed_List (Index); declare Old_Values : String_List_Access := new String_List'(Possible_Values_Of (Dummy, Old_Var)); New_Values : String_List_Access := new String_List'(Possible_Values_Of (Dummy, Var)); Values_Identical : Boolean := True; begin if Old_Values.all'Length /= New_Values.all'Length then Trace (Me_SV, "different amount of values"); Values_Identical := False; else Sort_Values (Old_Values.all); Sort_Values (New_Values.all); for I in Old_Values'Range loop if Old_Values (I).all /= New_Values (I).all then Trace (Me_SV, "Unmatched values: " & Old_Values (I).all & " and " & New_Values (I).all); Values_Identical := False; exit; end if; end loop; end if; Free (Old_Values); Free (New_Values); if not Values_Identical then if Old_Var.First_Project_Path = Var.First_Project_Path then -- Same project Trace (Me_SV, Project.Project_Path.Display_Full_Name & ": Scenario variables " & Get_Name_String (Old_Var.Var_Name) & " and " & Get_Name_String (Var.Var_Name) & " controlled by same external " & Ext_Ref_Name & " have different sets of possible values" & ASCII.LF); else -- Aggregated projects with same name Trace (Me_SV, "Scenario variables " & Get_Name_String (Old_Var.First_Project_Path) & ": " & Get_Name_String (Old_Var.Var_Name) & " and " & Project.Project_Path.Display_Full_Name & ": " & Get_Name_String (Var.Var_Name) & " controlled by same external " & Ext_Ref_Name & " have different sets of possible values" & ASCII.LF); end if; Inconsistent_SC_Externals.Include (Old_Var.Ext_Name); end if; end; Found := True; return; end if; end loop; Found := False; end Look_For_Duplicate_SVs; begin Trace (Me_SV, "Register_Scenario_Var " & Get_Name_String (GPR.Tree.Name_Of (Variable, T))); Var := Scenario_Variable' (Ext_Name => V, Var_Name => GPR.Tree.Name_Of (Variable, T), Default => External_Default (Project, Variable, Pkg, T), String_Type => String_Type_Of (Variable, T), Tree_Ref => T, Value => GPR.Ext.Value_Of (Tree.Env.Env.External, V, With_Default => External_Default (Project, Variable, Pkg, T)), First_Project_Path => Project.Data.View.Path.Display_Name); Look_For_Duplicate_SVs (N, Duplicate_Found); if Duplicate_Found then -- Nothing to add for the root one, however there may be some new -- nested ones. goto Unwind; end if; Typed_List (T_Curr) := Var; -- Ensure the external reference actually exists and has a valid -- value. Is_Valid := GPR.Ext.Value_Of (Tree.Env.Env.External, Var.Ext_Name) /= No_Name; if Is_Valid then declare Current : constant Name_Id := GPR.Ext.Value_Of (Tree.Env.Env.External, Var.Ext_Name); Iter : String_List_Iterator := Value_Of (T, Var); begin Is_Valid := False; while not Done (Iter) loop if Data (T, Iter) = Current then Is_Valid := True; exit; end if; Iter := Next (T, Iter); end loop; end; end if; if not Is_Valid then if Var.Default /= No_Name then GPR.Ext.Add (Tree.Env.Env.External, N, Get_Name_String (Var.Default), GPR.Ext.From_Command_Line); else GPR.Ext.Add (Tree.Env.Env.External, N, Get_Name_String (String_Value_Of (First_Literal_String (Var.String_Type, T), T)), GPR.Ext.From_Command_Line); end if; end if; T_Curr := T_Curr + 1; <> -- Unwinding nested external references if any. Increase_Indent (Me_SV, "Unwind nested external references"); declare Expression : Project_Node_Id; Expr : Project_Node_Id := Expression_Of (Variable, T); Ref : Name_Id; begin Expr := External_Default_Of (Current_Term (First_Term (Expr, T), T), T); Expression := Expr; while Expr /= Empty_Project_Node loop Expr := First_Term (Expr, T); if Next_Term (Expr, T) /= Empty_Project_Node then Decrease_Indent (Me_SV, "Unwind terminated: Not canonical nesting"); return; end if; Expr := Current_Term (Expr, T); if Kind_Of (Expr, T) = N_External_Value then Ref := String_Value_Of (External_Reference_Of (Expr, T), T); Trace (Me_SV, "Nested external reference: " & Get_Name_String (Ref)); Look_For_Duplicate_SVs (Get_Name_String (Ref), Duplicate_Found); if not Duplicate_Found then Var.Ext_Name := Ref; Var.Default := External_Default (Project, Variable, Pkg, T, Expression); Typed_List (T_Curr) := Var; T_Curr := T_Curr + 1; end if; Expr := External_Default_Of (Expr, T); Expression := Expr; else Decrease_Indent (Me_SV, "Unwind finished"); return; end if; end loop; end; end Register_Scenario_Var; -------------------------- -- Register_Untyped_Var -- -------------------------- procedure Register_Untyped_Var (Variable : Project_Node_Id; Proj : Project_Node_Id; Pkg : Project_Node_Id; Project : Project_Type) is pragma Unreferenced (Proj); T : constant GPR.Project_Node_Tree_Ref := Project.Data.Tree.Tree; V : constant Name_Id := External_Reference_Of (Variable, T); N : constant String := Get_String (V); Var : Untyped_Variable; begin Trace (Me_SV, "Register_Untyped_Var " & Get_Name_String (GPR.Tree.Name_Of (Variable, T))); for Index in 1 .. U_Curr - 1 loop if External_Name (Untyped_List (Index)) = N then -- Nothing to do return; end if; end loop; Var := Untyped_Variable' (Name => V, Default => External_Default (Project, Variable, Pkg, T), Value => GPR.Ext.Value_Of (Tree.Env.Env.External, V, With_Default => External_Default (Project, Variable, Pkg, T))); Untyped_List (U_Curr) := Var; U_Curr := U_Curr + 1; end Register_Untyped_Var; use Name_Id_Sets; use Ada.Containers; begin Trace (Me, "Compute the list of scenario variables"); Unchecked_Free (Tree.Env.Scenario_Variables); Unchecked_Free (Tree.Env.Untyped_Variables); Var_Quantity := Count_Vars; Typed_List := new Scenario_Variable_Array (1 .. Var_Quantity); T_Curr := Typed_List'First; Untyped_List := new Untyped_Variable_Array (1 .. Var_Quantity); U_Curr := Untyped_List'First; For_Each_External_Variable_Declaration (Tree.Root, Recursive => Recursive, Callback => Register_Var'Unrestricted_Access); if Inconsistent_SC_Externals.Length = 0 then if T_Curr > Typed_List'Last then Tree.Env.Scenario_Variables := Typed_List; else Tree.Env.Scenario_Variables := new Scenario_Variable_Array'(Typed_List (1 .. T_Curr - 1)); Unchecked_Free (Typed_List); end if; else -- Moving SVs with inconsistent types to UVs T_Curr2 := T_Curr - 1 - Integer (Inconsistent_SC_Externals.Length); Tree.Env.Scenario_Variables := new Scenario_Variable_Array (1 .. T_Curr2); T_Curr2 := 1; for I in 1 .. T_Curr - 1 loop if Inconsistent_SC_Externals.Contains (Typed_List (I).Ext_Name) then if Not_Already (Untyped_List, U_Curr, Typed_List (I).Ext_Name) then Untyped_List (U_Curr) := (Name => Typed_List (I).Ext_Name, Default => Typed_List (I).Default, Value => Typed_List (I).Value); U_Curr := U_Curr + 1; end if; else Tree.Env.Scenario_Variables (T_Curr2) := Typed_List (I); T_Curr2 := T_Curr2 + 1; end if; end loop; Unchecked_Free (Typed_List); Inconsistent_SC_Externals.Clear; end if; if U_Curr > Untyped_List'Last then Tree.Env.Untyped_Variables := Untyped_List; else Tree.Env.Untyped_Variables := new Untyped_Variable_Array'(Untyped_List (1 .. U_Curr - 1)); Unchecked_Free (Untyped_List); end if; end Compute_Scenario_Variables; ------------------------ -- Scenario_Variables -- ------------------------ function Scenario_Variables (Self : Project_Tree; Root_Only : Boolean := False) return Scenario_Variable_Array is begin return Scenario_Variables (Self.Data, Root_Only); end Scenario_Variables; ----------------------- -- Untyped_Variables -- ----------------------- function Untyped_Variables (Self : Project_Tree; Root_Only : Boolean := False) return Untyped_Variable_Array is begin return Untyped_Variables (Self.Data, Root_Only); end Untyped_Variables; ------------------------ -- Scenario_Variables -- ------------------------ function Scenario_Variables (Tree : Project_Tree_Data_Access; Root_Only : Boolean := False) return Scenario_Variable_Array is SVs : Scenario_Variable_Array_Access; UVs : Untyped_Variable_Array_Access; begin if Tree = null or else Tree.Is_Aggregated then return (1 .. 0 => <>); end if; if Root_Only then -- We need to save the actual values because otherwise -- Compute_Scenario_Variables will overwrite them. SVs := Tree.Env.Scenario_Variables; UVs := Tree.Env.Untyped_Variables; Tree.Env.Scenario_Variables := null; Tree.Env.Untyped_Variables := null; Compute_Scenario_Variables (Tree, Recursive => False); declare Result : constant Scenario_Variable_Array := Tree.Env.Scenario_Variables.all; begin Unchecked_Free (Tree.Env.Scenario_Variables); Unchecked_Free (Tree.Env.Untyped_Variables); Tree.Env.Scenario_Variables := SVs; Tree.Env.Untyped_Variables := UVs; return Result; end; end if; if Tree.Env.Scenario_Variables = null then Compute_Scenario_Variables (Tree); end if; for V of Tree.Env.Scenario_Variables.all loop V.Value := GPR.Ext.Value_Of (Tree.Env.Env.External, V.Ext_Name, With_Default => V.Default); end loop; return Tree.Env.Scenario_Variables.all; end Scenario_Variables; ----------------------- -- Untyped_Variables -- ----------------------- function Untyped_Variables (Tree : Project_Tree_Data_Access; Root_Only : Boolean := False) return Untyped_Variable_Array is SVs : Scenario_Variable_Array_Access; UVs : Untyped_Variable_Array_Access; begin if Tree = null or else Tree.Is_Aggregated then return (1 .. 0 => <>); end if; if Root_Only then -- We need to save the actual values because otherwise -- Compute_Scenario_Variables will overwrite them. SVs := Tree.Env.Scenario_Variables; UVs := Tree.Env.Untyped_Variables; Tree.Env.Scenario_Variables := null; Tree.Env.Untyped_Variables := null; Compute_Scenario_Variables (Tree, Recursive => False); declare Result : constant Untyped_Variable_Array := Tree.Env.Untyped_Variables.all; begin Unchecked_Free (Tree.Env.Scenario_Variables); Unchecked_Free (Tree.Env.Untyped_Variables); Tree.Env.Scenario_Variables := SVs; Tree.Env.Untyped_Variables := UVs; return Result; end; end if; if Tree.Env.Untyped_Variables = null then Compute_Scenario_Variables (Tree); end if; for V of Tree.Env.Untyped_Variables.all loop V.Value := GPR.Ext.Value_Of (Tree.Env.Env.External, V.Name, With_Default => V.Default); end loop; return Tree.Env.Untyped_Variables.all; end Untyped_Variables; ------------------------ -- Scenario_Variables -- ------------------------ function Scenario_Variables (Self : Project_Tree; External_Name : String; Root_Only : Boolean := False) return Scenario_Variable is E : constant String := External_Name; Ext : Name_Id; List : Scenario_Variable_Array_Access; Var : Scenario_Variable; SVs : Scenario_Variable_Array_Access; UVs : Untyped_Variable_Array_Access; SV : Scenario_Variable; begin Ext := Get_String (E); if Root_Only then -- We need to save the actual values because otherwise -- Compute_Scenario_Variables will overwrite them. SVs := Self.Data.Env.Scenario_Variables; UVs := Self.Data.Env.Untyped_Variables; Self.Data.Env.Scenario_Variables := null; Self.Data.Env.Untyped_Variables := null; Compute_Scenario_Variables (Self.Data, Recursive => False); for V of Self.Data.Env.Scenario_Variables.all loop if V.Ext_Name = Ext then SV := V; Unchecked_Free (Self.Data.Env.Scenario_Variables); Unchecked_Free (Self.Data.Env.Untyped_Variables); Self.Data.Env.Scenario_Variables := SVs; Self.Data.Env.Untyped_Variables := UVs; return SV; end if; end loop; Unchecked_Free (Self.Data.Env.Scenario_Variables); Unchecked_Free (Self.Data.Env.Untyped_Variables); Self.Data.Env.Scenario_Variables := SVs; Self.Data.Env.Untyped_Variables := UVs; return No_Variable; end if; if Self.Data.Env.Scenario_Variables = null then Compute_Scenario_Variables (Self.Data); end if; for V of Self.Data.Env.Scenario_Variables.all loop if V.Ext_Name = Ext then return V; end if; end loop; Var := Scenario_Variable' (Ext_Name => Ext, Var_Name => No_Name, Default => No_Name, String_Type => Empty_Project_Node, -- ??? Won't be able to edit it Tree_Ref => null, Value => No_Name, First_Project_Path => GPR.No_Path); List := Self.Data.Env.Scenario_Variables; Self.Data.Env.Scenario_Variables := new Scenario_Variable_Array' (Self.Data.Env.Scenario_Variables.all & Var); Unchecked_Free (List); return Var; end Scenario_Variables; -------------------------- -- Get_Untyped_Variable -- -------------------------- function Get_Untyped_Variable (Self : Project_Tree; External_Name : String; Root_Only : Boolean := False) return Untyped_Variable is Ext : Name_Id; List : Untyped_Variable_Array_Access; Var : Untyped_Variable; SVs : Scenario_Variable_Array_Access; UVs : Untyped_Variable_Array_Access; UV : Untyped_Variable; begin Ext := Get_String (External_Name); if Root_Only then -- We need to save the actual values because otherwise -- Compute_Scenario_Variables will overwrite them. SVs := Self.Data.Env.Scenario_Variables; UVs := Self.Data.Env.Untyped_Variables; Self.Data.Env.Scenario_Variables := null; Self.Data.Env.Untyped_Variables := null; Compute_Scenario_Variables (Self.Data, Recursive => False); for V of Self.Data.Env.Untyped_Variables.all loop if V.Name = Ext then UV := V; Unchecked_Free (Self.Data.Env.Scenario_Variables); Unchecked_Free (Self.Data.Env.Untyped_Variables); Self.Data.Env.Scenario_Variables := SVs; Self.Data.Env.Untyped_Variables := UVs; return UV; end if; end loop; Unchecked_Free (Self.Data.Env.Scenario_Variables); Unchecked_Free (Self.Data.Env.Untyped_Variables); Self.Data.Env.Scenario_Variables := SVs; Self.Data.Env.Untyped_Variables := UVs; return No_Untyped_Variable; end if; if Self.Data.Env.Scenario_Variables = null then Compute_Scenario_Variables (Self.Data); end if; for V of Self.Data.Env.Untyped_Variables.all loop if V.Name = Ext then return V; end if; end loop; Var := Untyped_Variable' (Name => Ext, Default => No_Name, Value => No_Name); List := Self.Data.Env.Untyped_Variables; Self.Data.Env.Untyped_Variables := new Untyped_Variable_Array' (Self.Data.Env.Untyped_Variables.all & Var); Unchecked_Free (List); return Var; end Get_Untyped_Variable; ------------------- -- External_Name -- ------------------- function External_Name (Var : Scenario_Variable) return String is begin return Get_String (Var.Ext_Name); end External_Name; ------------------- -- External_Name -- ------------------- function External_Name (Var : Untyped_Variable) return String is begin return Get_String (Var.Name); end External_Name; ---------------------- -- External_Default -- ---------------------- function External_Default (Var : Scenario_Variable) return String is begin return Get_String (Var.Default); end External_Default; ---------------------- -- External_Default -- ---------------------- function External_Default (Var : Untyped_Variable) return String is begin return Get_String (Var.Default); end External_Default; --------------- -- Set_Value -- --------------- procedure Set_Value (Var : in out Scenario_Variable; Value : String) is begin Var.Value := Get_String (Value); end Set_Value; --------------- -- Set_Value -- --------------- procedure Set_Value (Var : in out Untyped_Variable; Value : String) is begin Var.Value := Get_String (Value); end Set_Value; ------------------------ -- Change_Environment -- ------------------------ procedure Change_Environment (Self : Project_Tree; Vars : Scenario_Variable_Array; UVars : Untyped_Variable_Array := Empty_Untyped_Variable_Array) is begin for V in Vars'Range loop GPR.Ext.Add (Self.Data.Env.Env.External, Get_String (Vars (V).Ext_Name), Get_String (Vars (V).Value), GPR.Ext.From_Command_Line); end loop; for V in UVars'Range loop GPR.Ext.Add (Self.Data.Env.Env.External, Get_String (UVars (V).Name), Get_String (UVars (V).Value), GPR.Ext.From_Command_Line); end loop; end Change_Environment; ------------------------ -- Change_Environment -- ------------------------ procedure Change_Environment (Self : Project_Environment; Name, Value : String) is begin GPR.Ext.Add (Self.Env.External, Name, Value, GPR.Ext.From_Command_Line); end Change_Environment; ----------- -- Value -- ----------- function Value (Self : Project_Environment; Name : String) return String is V : Name_Id; begin V := GPR.Ext.Value_Of (Self.Env.External, Get_String (Name)); if V /= No_Name then return Get_String (V); else return ""; end if; end Value; ----------- -- Value -- ----------- function Value (Var : Scenario_Variable) return String is begin return Get_String (Var.Value); end Value; ----------- -- Value -- ----------- function Value (Var : Untyped_Variable) return String is begin return Get_String (Var.Value); end Value; -------------- -- Get_View -- -------------- function Get_View (Tree : GPR.Project_Tree_Ref; Path : Path_Name_Type) return GPR.Project_Id is Proj : Project_List := Tree.Projects; begin while Proj /= null loop if Proj.Project.Path.Display_Name = Path and then Proj.Project.Qualifier /= Configuration then return Proj.Project; end if; Proj := Proj.Next; end loop; return GPR.No_Project; end Get_View; -------------- -- Get_View -- -------------- function Get_View (Project : Project_Type'Class) return GPR.Project_Id is begin if Project.Data = null or else Project.Data.Node = Empty_Project_Node then return GPR.No_Project; elsif Project.Data.View = GPR.No_Project then Project.Data.View := Get_View (Project.Tree_View, GPR.Tree.Path_Name_Of (Project.Data.Node, Project.Tree_Tree)); end if; return Project.Data.View; end Get_View; -------------------------------------------- -- For_Each_External_Variable_Declaration -- -------------------------------------------- procedure For_Each_External_Variable_Declaration (Root_Project : Project_Type; Recursive : Boolean; Callback : External_Variable_Callback) is Iterator : Project_Iterator := Start (Root_Project, Recursive); Current_Project : Project_Type; Tree : GPR.Project_Node_Tree_Ref; Var : Project_Node_Id; Pkg : Project_Node_Id; Prj : Project_Node_Id; begin loop Current_Project := Current (Iterator); exit when Current_Project.Data = null; Tree := Current_Project.Data.Tree.Tree; Pkg := Current_Project.Data.Node; Prj := Current_Project.Data.Node; Current_Project.Data.Uses_Variables := False; -- For all the packages and the common section while Pkg /= Empty_Project_Node loop Var := First_Variable_Of (Pkg, Tree); while Var /= Empty_Project_Node loop if Kind_Of (Var, Tree) in N_Typed_Variable_Declaration | N_Variable_Declaration and then Is_External_Variable (Var, Tree) then Callback (Var, Prj, Pkg, Current_Project); end if; if Kind_Of (Var, Tree) = N_Variable_Declaration or else (Kind_Of (Var, Tree) = N_Typed_Variable_Declaration and then not Is_External_Variable (Var, Tree)) then if Active (Debug) then Trace (Me, "Uses variable in " & Current_Project.Name); Pretty_Print (Var, Tree, Backward_Compatibility => False); end if; Current_Project.Data.Uses_Variables := True; end if; Var := Next_Variable (Var, Tree); end loop; if Pkg = Prj then Pkg := First_Package_Of (Prj, Tree); else Pkg := Next_Package_In_Project (Pkg, Tree); end if; end loop; Next (Iterator); end loop; end For_Each_External_Variable_Declaration; -------------- -- Switches -- -------------- procedure Switches (Project : Project_Type; In_Pkg : String; File : GNATCOLL.VFS.Virtual_File; Language : String; Value : out GNAT.Strings.String_List_Access; Is_Default_Value : out Boolean) is Val : Variable_Value; begin if Get_View (Project) /= GPR.No_Project then GPR.Util.Get_Switches (Source_File => File_Name_Type (Get_String (File.Display_Base_Name)), Source_Lang => Get_String (Language), Source_Prj => Project.Data.View, Pkg_Name => Get_String (To_Lower (In_Pkg)), Project_Tree => Project.Data.Tree.View, Value => Val, Is_Default => Is_Default_Value); Value := Variable_Value_To_List (Project, Val); else Value := null; end if; if Value = null then -- No switches Value := new String_List'(1 .. 0 => null); end if; end Switches; -------------- -- Value_Of -- -------------- function Value_Of (Tree : GPR.Project_Node_Tree_Ref; Var : Scenario_Variable) return String_List_Iterator is V, Expr : Project_Node_Id; begin case Kind_Of (Var.String_Type, Tree) is when N_String_Type_Declaration => return (Current => First_Literal_String (Var.String_Type, Tree)); when N_Attribute_Declaration | N_Typed_Variable_Declaration | N_Variable_Declaration => V := Expression_Of (Var.String_Type, Tree); case Kind_Of (V, Tree) is when N_Expression => Expr := First_Term (V, Tree); pragma Assert (Kind_Of (Expr, Tree) = N_Term); Expr := Current_Term (Expr, Tree); case Kind_Of (Expr, Tree) is when N_Literal_String_List => return (Current => First_Expression_In_List (Expr, Tree)); when N_External_Value => return (Current => External_Default_Of (Expr, Tree)); when others => return (Current => V); end case; when others => raise Program_Error; end case; when others => raise Program_Error; end case; end Value_Of; ---------- -- Done -- ---------- function Done (Iter : String_List_Iterator) return Boolean is begin return Iter.Current = Empty_Project_Node; end Done; ---------- -- Next -- ---------- function Next (Tree : GPR.Project_Node_Tree_Ref; Iter : String_List_Iterator) return String_List_Iterator is begin pragma Assert (Iter.Current /= Empty_Project_Node); case Kind_Of (Iter.Current, Tree) is when N_Literal_String => return (Current => Next_Literal_String (Iter.Current, Tree)); when N_Expression => return (Current => Next_Expression_In_List (Iter.Current, Tree)); when others => raise Program_Error; end case; end Next; ---------- -- Data -- ---------- function Data (Tree : GPR.Project_Node_Tree_Ref; Iter : String_List_Iterator) return GPR.Name_Id is begin pragma Assert (Kind_Of (Iter.Current, Tree) = N_Literal_String); return String_Value_Of (Iter.Current, Tree); end Data; ------------------------ -- Possible_Values_Of -- ------------------------ function Possible_Values_Of (Self : Project_Tree; Var : Scenario_Variable) return String_List is pragma Unreferenced (Self); Tree : constant GPR.Project_Node_Tree_Ref := Var.Tree_Ref; Count : Natural := 0; Iter : String_List_Iterator := Value_Of (Tree, Var); begin while not Done (Iter) loop Count := Count + 1; Iter := Next (Tree, Iter); end loop; declare Values : String_List (1 .. Count); begin Count := Values'First; Iter := Value_Of (Tree, Var); while not Done (Iter) loop Values (Count) := new String' (Get_Name_String (Data (Tree, Iter))); Count := Count + 1; Iter := Next (Tree, Iter); end loop; return Values; end; end Possible_Values_Of; --------------------------- -- Has_Imported_Projects -- --------------------------- function Has_Imported_Projects (Project : Project_Type) return Boolean is Iter : constant Inner_Project_Iterator := Start (Project, Recursive => True, Direct_Only => True); begin return Current (Iter) /= No_Project; end Has_Imported_Projects; --------- -- "=" -- --------- function "=" (Prj1, Prj2 : Project_Type) return Boolean is begin if Prj1.Data = null then return Prj2.Data = null; elsif Prj2.Data = null then return False; else return Prj1.Data.Node = Prj2.Data.Node and then Prj1.Data.Tree = Prj2.Data.Tree; end if; end "="; --------- -- "<" -- --------- function Less (L, R : File_Info_Abstract'Class) return Boolean is begin return L < R; end Less; --------- -- "<" -- --------- function "<" (L, R : File_Info) return Boolean is begin return L.Project.Project_Path < R.Project.Project_Path; end "<"; ---------------------- -- Extended_Project -- ---------------------- function Extended_Project (Project : Project_Type) return Project_Type is Tree : constant GPR.Project_Node_Tree_Ref := Project.Data.Tree.Tree; Extended : constant Project_Node_Id := Extended_Project_Of (Project_Declaration_Of (Project.Data.Node, Tree), Tree); begin if Extended = Empty_Project_Node then return No_Project; else return Project_Type (Project_From_Name (Project.Data.Tree, GPR.Tree.Name_Of (Extended, Tree))); end if; end Extended_Project; ------------------------------------ -- Extended_Projects_Source_Files -- ------------------------------------ function Extended_Projects_Source_Files (Project : Project_Type) return GNATCOLL.VFS.File_Array_Access is P : Project_Type := Project; Result, Files : GNATCOLL.VFS.File_Array_Access; begin if Project.Data = null or else Project.Data.Files = null then return new File_Array (1 .. 0); end if; while P /= No_Project loop Files := P.Source_Files (Recursive => False); Append (Result, Files.all); Unchecked_Free (Files); P := Extended_Project (P); end loop; return Result; end Extended_Projects_Source_Files; ----------------------- -- Extending_Project -- ----------------------- function Extending_Project (Project : Project_Type; Recurse : Boolean := False) return Project_Type is Tree : constant GPR.Project_Node_Tree_Ref := Project.Data.Tree.Tree; Extending : Project_Node_Id := Empty_Project_Node; Extended : Project_Node_Id := Project.Data.Node; begin while Project_Declaration_Of (Extended, Tree) /= Empty_Project_Node loop Extending := Extending_Project_Of (Project_Declaration_Of (Extended, Tree), Tree); exit when not Recurse; -- Case of following extension chain: if we reached the of the chain, -- go back one step (to the last non-empty node) and exit. if Extending = Empty_Project_Node then Extending := Extended; exit; end if; -- Iterate Extended := Extending; end loop; if Extending = Empty_Project_Node then return No_Project; else return Project_Type (Project_From_Path (Project.Data.Tree, GPR.Tree.Path_Name_Of (Extending, Tree))); end if; end Extending_Project; ---------------------- -- Externally_Built -- ---------------------- function Externally_Built (Project : Project_Type) return Boolean is begin return Get_View (Project).Externally_Built; end Externally_Built; ----------- -- Build -- ----------- function Build (Package_Name, Attribute_Name : String) return Attribute_Pkg_String is begin return Attribute_Pkg_String (To_Lower (Package_Name) & '#' & To_Lower (Attribute_Name)); end Build; function Build (Package_Name, Attribute_Name : String) return Attribute_Pkg_List is begin return Attribute_Pkg_List (To_Lower (Package_Name) & '#' & To_Lower (Attribute_Name)); end Build; ------------------------ -- Delete_File_Suffix -- ------------------------ function Delete_File_Suffix (Filename : Filesystem_String; Project : Project_Type) return Natural is View : constant Project_Id := Get_View (Project); Lang : Language_Ptr; Suffix : Name_Id; begin -- View will be null when called from the project wizard if View /= GPR.No_Project then Lang := View.Languages; while Lang /= null loop Suffix := Name_Id (Lang.Config.Naming_Data.Spec_Suffix); if Suffix /= No_Name and then Ends_With (+Filename, Get_Name_String (Suffix)) then return Filename'Last - Natural (Length_Of_Name (Suffix)); end if; Suffix := Name_Id (Lang.Config.Naming_Data.Body_Suffix); if Suffix /= No_Name and then Ends_With (+Filename, Get_Name_String (Suffix)) then return Filename'Last - Natural (Length_Of_Name (Suffix)); end if; Lang := Lang.Next; end loop; end if; -- Check the default naming scheme as well ? Otherwise, it might happen -- that a project has its own naming scheme, but still references files -- in the runtime with the default naming scheme. declare Ext : constant String := GNAT.Directory_Operations.File_Extension (+Filename); begin if Ext = ".ads" or else Ext = ".adb" then return Filename'Last - 4; end if; end; return Filename'Last; end Delete_File_Suffix; --------------------- -- Executable_Name -- --------------------- function Executable_Name (Project : Project_Type; File : GNATCOLL.VFS.Filesystem_String; Include_Suffix : Boolean := False) return Filesystem_String is Base : constant Filesystem_String := Base_Name (File); Exec_Name : File_Name_Type; Main_Source : Source_Id; begin if Project = No_Project then Trace (Me, "Executable_Name: no project"); -- Simply remove the current extension, since we don't have any -- information on the file itself. return Base (Base'First .. Delete_File_Suffix (Base, Project)); else declare Norm : String := +Base; begin Osint.Canonical_Case_File_Name (Norm); Main_Source := Find_Source (In_Tree => Project.Data.Tree.View, Project => Project.Data.View, Base_Name => File_Name_Type (Get_String (Norm))); end; if Main_Source = No_Source then Trace (Me, "Executable_Name: source not found (" & (+Base) & ')'); return Base (Base'First .. Delete_File_Suffix (Base, Project)); end if; -- Do not include the suffix: it might be incorrect if we user will -- actually use a cross-compiler, since the suffix's default value -- depends on the host. Exec_Name := Executable_Of (Project => Project.Data.View, Shared => Project.Data.Tree.View.Shared, Main => Main_Source.File, Index => Main_Source.Index, Language => Get_Name_String (Main_Source.Language.Name), Include_Suffix => Include_Suffix); return +(Get_String (Exec_Name)); end if; end Executable_Name; ------------------ -- Create_Flags -- ------------------ function Create_Flags (On_Error : GPR.Error_Handler; Require_Sources : Boolean := True; Ignore_Missing_With : Boolean := False; Report_Missing_Dirs : Boolean := True) return Processing_Flags is begin if Require_Sources then return Create_Flags (Report_Error => On_Error, When_No_Sources => Warning, Require_Sources_Other_Lang => True, Compiler_Driver_Mandatory => False, Allow_Duplicate_Basenames => True, Require_Obj_Dirs => (if Report_Missing_Dirs then Warning else Silent), Allow_Invalid_External => Warning, Missing_Source_Files => Warning, Ignore_Missing_With => Ignore_Missing_With); else return Create_Flags (Report_Error => On_Error, When_No_Sources => Silent, Require_Sources_Other_Lang => False, Compiler_Driver_Mandatory => False, Allow_Duplicate_Basenames => True, Require_Obj_Dirs => (if Report_Missing_Dirs then Warning else Silent), Allow_Invalid_External => Silent, Missing_Source_Files => Warning, Ignore_Missing_With => Ignore_Missing_With); end if; end Create_Flags; ---------------------------- -- Has_Multi_Unit_Sources -- ---------------------------- function Has_Multi_Unit_Sources (Project : Project_Type) return Boolean is View : constant Project_Id := Get_View (Project); begin if View /= GPR.No_Project then return View.Has_Multi_Unit_Sources; end if; return False; end Has_Multi_Unit_Sources; ----------------------- -- Project_From_Name -- ----------------------- function Project_From_Name (Tree : Project_Tree_Data_Access; Name : GPR.Name_Id) return Project_Type'Class is Tree_For_Map : Project_Tree_Data_Access; P_Cursor, P_Found : Project_Htables.Cursor; Name_Found : Boolean := False; -- Name is a base name (for now), but the htable is indexed on the -- full path of the project. So we need to traverse all its elements. -- In the case of aggregate projects, we return No_Project if multiple -- projects match. Normalized : constant Filesystem_String := Create (+Get_String (Name)).Base_Name (Suffix => +GPR.Project_File_Extension, Normalize => True); -- The name of a project is not related to file names, and is always -- case-insensitive. So we convert to lower-case here. However, if we -- want a version of Project_From_Name that takes a path, we will need -- to use the filesystem's casing. -- -- We can't compare project names and file names, because child projects -- have names like "p.main" when the file name is "p-main". N : constant String := To_Lower (+Normalized); begin if Tree = null or else Tree.Tree = null then Trace (Me, "Project_From_Name: Registry not initialized"); return No_Project; else Tree_For_Map := Tree.Root.Data.Tree_For_Map; P_Cursor := Tree_For_Map.Projects.First; if Project_Qualifier_Of (Tree.Root.Data.Node, Tree.Tree) = GPR.Aggregate then while P_Cursor /= Project_Htables.No_Element loop if To_Lower (Element (P_Cursor).Name) = N then if Name_Found then Trace (Me, "Multiple projects with same name (" & N & ')'); return No_Project; else Name_Found := True; P_Found := P_Cursor; end if; end if; Next (P_Cursor); end loop; if Name_Found then return Element (P_Found); end if; else while P_Cursor /= Project_Htables.No_Element loop if To_Lower (Element (P_Cursor).Name) = N then return Element (P_Cursor); end if; Next (P_Cursor); end loop; end if; Trace (Me, "Get_Project_From_Name: " & Get_String (Name) & " wasn't found"); return No_Project; end if; end Project_From_Name; ----------------------- -- Project_From_Name -- ----------------------- function Project_From_Name (Self : Project_Tree'Class; Name : String) return Project_Type is begin return Project_Type (Project_From_Name (Self.Data, Get_String (Name))); end Project_From_Name; ----------------------- -- Project_From_Path -- ----------------------- function Project_From_Path (Self : Project_Tree'Class; Path : Virtual_File) return Project_Type is Tree_For_Map : constant Project_Tree_Data_Access := Self.Data.Root.Data.Tree_For_Map; -- An access to the root tree VF : constant GNATCOLL.VFS.Virtual_File := Create (Normalize_Pathname (Path.Full_Name, Resolve_Links => False)); P_Cursor : constant Project_Htables.Cursor := Tree_For_Map.Projects.Find (VF); begin if not Has_Element (P_Cursor) then return No_Project; end if; return Element (P_Cursor); end Project_From_Path; ----------------------- -- Project_From_Path -- ----------------------- function Project_From_Path (Tree : Project_Tree_Data_Access; Path_Id : Path_Name_Type) return Project_Type'Class is Tree_For_Map : constant Project_Tree_Data_Access := Tree.Root.Data.Tree_For_Map; -- An access to the root tree P_Cursor : constant Project_Htables.Cursor := Tree_For_Map.Projects.Find (Create (+Get_String (Path_Id))); begin if not Has_Element (P_Cursor) then return No_Project; end if; return Element (P_Cursor); end Project_From_Path; ---------------------- -- Set_Trusted_Mode -- ---------------------- procedure Set_Trusted_Mode (Self : in out Project_Environment; Trusted : Boolean := True) is begin Self.Trusted_Mode := Trusted; Opt.Follow_Links_For_Files := not Trusted; Opt.Follow_Links_For_Dirs := not Trusted; GNATCOLL.VFS.Symbolic_Links_Support (Active => not Trusted); end Set_Trusted_Mode; ------------------ -- Trusted_Mode -- ------------------ function Trusted_Mode (Self : Project_Environment) return Boolean is begin return Self.Trusted_Mode; end Trusted_Mode; -------------------------------- -- Set_Predefined_Source_Path -- -------------------------------- procedure Set_Predefined_Source_Path (Self : in out Project_Environment; Path : GNATCOLL.VFS.File_Array) is begin Unchecked_Free (Self.Predefined_Source_Files); Unchecked_Free (Self.Predefined_Source_Path); Self.Predefined_Source_Path := new File_Array'(Path); end Set_Predefined_Source_Path; procedure Set_Predefined_Object_Path (Self : in out Project_Environment; Path : GNATCOLL.VFS.File_Array) is begin Unchecked_Free (Self.Predefined_Object_Path); Self.Predefined_Object_Path := new File_Array'(Path); end Set_Predefined_Object_Path; procedure Set_Predefined_Project_Path (Self : in out Project_Environment; Path : GNATCOLL.VFS.File_Array) is begin Unchecked_Free (Self.Predefined_Project_Path); Self.Predefined_Project_Path := new File_Array'(Path); end Set_Predefined_Project_Path; ---------------------------- -- Predefined_Source_Path -- ---------------------------- function Predefined_Source_Path (Self : Project_Environment) return GNATCOLL.VFS.File_Array is begin if Self.Predefined_Source_Path = null then return (1 .. 0 => GNATCOLL.VFS.No_File); else return Self.Predefined_Source_Path.all; end if; end Predefined_Source_Path; function Predefined_Object_Path (Self : Project_Environment) return GNATCOLL.VFS.File_Array is begin if Self.Predefined_Object_Path = null then return (1 .. 0 => GNATCOLL.VFS.No_File); else return Self.Predefined_Object_Path.all; end if; end Predefined_Object_Path; function Predefined_Project_Path (Self : Project_Environment) return GNATCOLL.VFS.File_Array is Current : Virtual_File; begin if Self.Predefined_Project_Path = null then Current := Create (Get_Current_Dir); return (1 .. 1 => Current); else return Self.Predefined_Project_Path.all; end if; end Predefined_Project_Path; ------------------------ -- Set_Build_Tree_Dir -- ------------------------ procedure Set_Build_Tree_Dir (Self : in out Project_Environment; Dir : GNATCOLL.VFS.Filesystem_String) is pragma Unreferenced (Self); begin Free (GPR.Build_Tree_Dir); if Dir = "" then GPR.Build_Tree_Dir := null; else GPR.Build_Tree_Dir := new String'(+Dir); end if; end Set_Build_Tree_Dir; -------------------- -- Build_Tree_Dir -- -------------------- function Build_Tree_Dir (Self : Project_Environment) return GNATCOLL.VFS.Filesystem_String is pragma Unreferenced (Self); begin if GPR.Build_Tree_Dir = null then return ""; else return +GPR.Build_Tree_Dir.all; end if; end Build_Tree_Dir; ------------------ -- Set_Root_Dir -- ------------------ procedure Set_Root_Dir (Self : in out Project_Environment; Dir : GNATCOLL.VFS.Filesystem_String) is pragma Unreferenced (Self); begin Free (GPR.Root_Dir); if Dir = "" then GPR.Root_Dir := null; else GPR.Root_Dir := new String'(+Dir); end if; end Set_Root_Dir; -------------- -- Root_Dir -- -------------- function Root_Dir (Self : Project_Environment) return GNATCOLL.VFS.Filesystem_String is pragma Unreferenced (Self); begin if GPR.Root_Dir = null then return ""; else return +GPR.Root_Dir.all; end if; end Root_Dir; ----------------------- -- Set_Object_Subdir -- ----------------------- procedure Set_Object_Subdir (Self : in out Project_Environment; Subdir : GNATCOLL.VFS.Filesystem_String) is pragma Unreferenced (Self); begin Free (GPR.Subdirs); if Subdir = "." then GPR.Subdirs := null; else GPR.Subdirs := new String'(+Subdir); end if; end Set_Object_Subdir; ------------------- -- Object_Subdir -- ------------------- function Object_Subdir (Self : Project_Environment) return GNATCOLL.VFS.Filesystem_String is pragma Unreferenced (Self); begin if GPR.Subdirs = null then return ""; else return +GPR.Subdirs.all; end if; end Object_Subdir; ---------------------- -- Set_Xrefs_Subdir -- ---------------------- procedure Set_Xrefs_Subdir (Self : in out Project_Environment; Subdir : GNATCOLL.VFS.Filesystem_String) is begin Free (Self.Xrefs_Subdir); Self.Xrefs_Subdir := new String'(+Subdir); end Set_Xrefs_Subdir; function Xrefs_Subdir (Self : Project_Environment) return GNATCOLL.VFS.Filesystem_String is begin if Self.Xrefs_Subdir = null then return ""; else return +Self.Xrefs_Subdir.all; end if; end Xrefs_Subdir; ----------------------------- -- Predefined_Source_Files -- ----------------------------- function Predefined_Source_Files (Self : access Project_Environment) return GNATCOLL.VFS.File_Array is begin -- ??? A nicer way would be to implement this with a predefined project, -- and rely on the project parser to return the source -- files. Unfortunately, this doesn't work with the current -- implementation of this parser, since one cannot have two separate -- project hierarchies at the same time. if Self.Predefined_Source_Files = null and then Self.Predefined_Source_Path /= null then Self.Predefined_Source_Files := Read_Files_From_Dirs (Self.Predefined_Source_Path.all); end if; if Self.Predefined_Source_Files = null then return Empty_File_Array; else return Self.Predefined_Source_Files.all; end if; end Predefined_Source_Files; ------------------ -- Data_Factory -- ------------------ function Data_Factory (Self : Project_Tree) return Project_Data_Access is pragma Unreferenced (Self); begin return new Project_Data; end Data_Factory; ---------- -- Data -- ---------- function Data (Project : Project_Type) return Project_Data_Access is begin return Project.Data; end Data; ------------- -- On_Free -- ------------- procedure On_Free (Self : in out Project_Data) is begin Unchecked_Free (Self.Imported_Projects.Items); Unchecked_Free (Self.Importing_Projects); Reset_View (Self); end On_Free; ---------------- -- Reset_View -- ---------------- procedure Reset_View (Self : in out Project_Data'Class) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Basename_To_Info_Cache.Map, Basename_To_Info_Cache_Map_Access); begin Self.View := GPR.No_Project; -- No need to reset Self.Imported_Projects, since this doesn't -- change when the view changes. Unchecked_Free (Self.Non_Recursive_Include_Path); Unchecked_Free (Self.Files); if Self.Base_Name_To_Full_Path /= null then Self.Base_Name_To_Full_Path.Clear; Unchecked_Free (Self.Base_Name_To_Full_Path); Self.Base_Name_To_Full_Path := null; end if; Self.View_Is_Complete := True; end Reset_View; ------------ -- Adjust -- ------------ overriding procedure Adjust (Self : in out Project_Type) is begin if Self.Data /= null then Self.Data.Refcount := Self.Data.Refcount + 1; end if; end Adjust; -------------- -- Finalize -- -------------- overriding procedure Finalize (Self : in out Project_Type) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Project_Data'Class, Project_Data_Access); Data : Project_Data_Access := Self.Data; begin -- Make Finalize idempotent, since it could be called several times. -- See RM 7.6.1 (24) Self.Data := null; -- We never finalize unless Tree is null: the tree is set to null when -- the project_tree is unloaded. That means user cares about memory -- management. If we try to finalize when unload hasn't been called, and -- because the tree owns references to the project, this means Finalize -- is called by GNAT as part of processing the finalization_lists. In -- that case, it seems we always end up in a case where we access -- already deallocated memory. if Data /= null then Data.Refcount := Data.Refcount - 1; if Data.Refcount = 0 and then Data.Tree = null then On_Free (Data.all); Unchecked_Free (Data); Data := null; end if; end if; end Finalize; ---------------------------- -- Add_Language_Extension -- ---------------------------- procedure Add_Language_Extension (Self : in out Project_Environment; Language_Name : String; Extension : String) is Ext : String := Extension; begin Osint.Canonical_Case_File_Name (Ext); Self.Extensions.Include (Ext, Get_String (To_Lower (Language_Name))); end Add_Language_Extension; ----------------------------------------- -- Register_Default_Language_Extension -- ----------------------------------------- procedure Register_Default_Language_Extension (Self : in out Project_Environment; Language_Name : String; Default_Spec_Suffix : String; Default_Body_Suffix : String; Obj_Suffix : String := ".o") is Spec, Impl, Obj : String_Access; Spec_Suff : String := Default_Spec_Suffix; Impl_Suff : String := Default_Body_Suffix; begin -- GNAT doesn't allow empty suffixes, and will display an error when -- the view is recomputed, in that case. Therefore we substitute dummy -- empty suffixes instead if Default_Spec_Suffix = "" then Spec := new String'(Dummy_Suffix); else Osint.Canonical_Case_File_Name (Spec_Suff); Spec := new String'(Spec_Suff); end if; if Default_Body_Suffix = "" then Impl := new String'(Dummy_Suffix); else Osint.Canonical_Case_File_Name (Impl_Suff); Impl := new String'(Impl_Suff); end if; if Obj_Suffix = "" then Obj := new String'("-"); else Obj := new String'(Obj_Suffix); end if; Self.Naming_Schemes := new Naming_Scheme_Record' (Language => new String'(To_Lower (Language_Name)), Default_Spec_Suffix => Spec, Default_Body_Suffix => Impl, Obj_Suffix => Obj, Next => Self.Naming_Schemes); end Register_Default_Language_Extension; ------------------------- -- Default_Spec_Suffix -- ------------------------- function Default_Spec_Suffix (Self : Project_Environment'Class; Language_Name : String) return String is Tmp : Naming_Scheme_Access := Self.Naming_Schemes; Lang : constant String := To_Lower (Language_Name); begin while Tmp /= null loop if Tmp.Language.all = Lang then return Tmp.Default_Spec_Suffix.all; end if; Tmp := Tmp.Next; end loop; return ""; end Default_Spec_Suffix; ------------------------- -- Default_Body_Suffix -- ------------------------- function Default_Body_Suffix (Self : Project_Environment'Class; Language_Name : String) return String is Tmp : Naming_Scheme_Access := Self.Naming_Schemes; Lang : constant String := To_Lower (Language_Name); begin while Tmp /= null loop if Tmp.Language.all = Lang then return Tmp.Default_Body_Suffix.all; end if; Tmp := Tmp.Next; end loop; return ""; end Default_Body_Suffix; --------------------------- -- Registered_Extensions -- --------------------------- function Registered_Extensions (Self : Project_Environment; Language_Name : String) return GNAT.Strings.String_List is Lang : constant String := To_Lower (Language_Name); Lang_Id : constant Name_Id := Get_String (Lang); Iter : Extensions_Languages.Cursor := Self.Extensions.First; Count : Natural := 0; begin while Has_Element (Iter) loop if Element (Iter) = Lang_Id then Count := Count + 1; end if; Next (Iter); end loop; declare Args : String_List (1 .. Count); begin Count := Args'First; Iter := Self.Extensions.First; while Has_Element (Iter) loop if Element (Iter) = Lang_Id then Args (Count) := new String'(Key (Iter)); Count := Count + 1; end if; Next (Iter); end loop; return Args; end; end Registered_Extensions; ------------------ -- Root_Project -- ------------------ function Root_Project (Self : Project_Tree'Class) return Project_Type is begin if Self.Data = null then return No_Project; else return Self.Data.Root; end if; end Root_Project; ---------------------------------- -- Directory_Belongs_To_Project -- ---------------------------------- function Directory_Belongs_To_Project (Self : Project_Tree; Directory : GNATCOLL.VFS.Filesystem_String; Direct_Only : Boolean := True) return Boolean is Curs : constant Directory_Statuses.Cursor := Self.Data.Directories.Find (Name_As_Directory (Directory)); Belong : Directory_Dependency; begin if Has_Element (Curs) then Belong := Element (Curs); return Belong = Direct or else (not Direct_Only and then Belong = As_Parent); else return False; end if; end Directory_Belongs_To_Project; ---------- -- Hash -- ---------- function Hash (File : GNATCOLL.VFS.Filesystem_String) return Ada.Containers.Hash_Type is begin if GNATCOLL.VFS_Utils.Local_Host_Is_Case_Sensitive then return Ada.Strings.Hash (+File); else return Ada.Strings.Hash_Case_Insensitive (+File); end if; end Hash; function Hash (Node : Project_Node_Id) return Ada.Containers.Hash_Type is begin return Ada.Containers.Hash_Type (GPR.Tree.Hash (Node)); end Hash; ------------------ -- Include_File -- ------------------ procedure Include_File (Map : in out Names_Files.Map; Key : GNATCOLL.VFS.Filesystem_String; Elem : Source_File_Data) is M_Cur : Names_Files.Cursor; Inserted : Boolean; Elem_Access : Source_File_Data_Access; begin Map.Insert (Key, Elem, M_Cur, Inserted); if Inserted then return; end if; declare Found_Elem : constant Names_Files.Reference_Type := Map.Reference (M_Cur); begin if Found_Elem.Project = Elem.Project and then Found_Elem.File = Elem.File then -- Exactly same file, nothing has to be done. return; elsif Found_Elem.Next = null then Found_Elem.Next := new Source_File_Data'(Elem); else -- Look through other files with same base name and add elem -- if not present. Elem_Access := Found_Elem.Next; loop if Elem_Access.Project = Elem.Project and then Elem_Access.File = Elem.File then return; end if; if Elem_Access.Next = null then Elem_Access.Next := new Source_File_Data'(Elem); return; end if; Elem_Access := Elem_Access.Next; end loop; end if; end; end Include_File; ----------- -- Equal -- ----------- function Equal (F1, F2 : GNATCOLL.VFS.Filesystem_String) return Boolean is begin -- ??? In GPS, we used to take into account the sensitive of the build -- host. However, this wasn't correct either, because it was computed -- at elaboration time, so always with local_host. Ideally, we should -- have access to a Project_Environment to find this out. return Equal (+F1, +F2, Case_Sensitive => GNATCOLL.VFS_Utils.Local_Host_Is_Case_Sensitive); end Equal; ---------------------- -- Reload_If_Needed -- ---------------------- procedure Reload_If_Needed (Self : in out Project_Tree; Reloaded : out Boolean; Recompute_View : Boolean := False; Errors : Error_Report := null) is Iter : Inner_Project_Iterator; begin Iter := Start (Self.Root_Project); Reloaded := False; while Current (Iter) /= No_Project loop if File_Time_Stamp (Project_Path (Current (Iter))) > Self.Data.Timestamp then Trace (Me, "Reload_If_Needed: timestamp has changed for " & Current (Iter).Name); Reloaded := True; exit; end if; Next (Iter); end loop; if Reloaded then Self.Load (Env => Self.Data.Env, Root_Project_Path => Project_Path (Self.Root_Project), Recompute_View => Recompute_View, Errors => Errors); else Trace (Me, "Reload_If_Needed: nothing to do, timestamp unchanged"); end if; end Reload_If_Needed; ---------- -- Load -- ---------- procedure Load (Self : in out Project_Tree; Root_Project_Path : GNATCOLL.VFS.Virtual_File; Env : Project_Environment_Access := null; Packages_To_Check : GNAT.Strings.String_List_Access := No_Packs; Errors : Error_Report := null; Recompute_View : Boolean := True; Report_Missing_Dirs : Boolean := True) is Block_Me : constant Block_Trace_Handle := Create (Me, Root_Project_Path.Display_Full_Name); Tmp : Project_Tree'Class := Self; -- Must use same tag Previous_Project : Virtual_File; Previous_Status : Project_Status; Success : Boolean; Project : Project_Node_Id; Project_File : GNATCOLL.VFS.Virtual_File := Root_Project_Path; Pth : Path_Name_Type; begin Sinput.Clear_Source_File_Table; Sinput.Reset_First; if Active (Me_Gnat) then GPR.Current_Verbosity := GPR.High; end if; Set_Host_Targets_List; if Self.Data /= null and then Self.Data.Root /= No_Project then Previous_Project := Self.Root_Project.Project_Path; Previous_Status := Self.Data.Status; else Previous_Project := GNATCOLL.VFS.No_File; Previous_Status := Default; end if; if Env /= null and then Env.Config_File.Is_Regular_File then Env.Set_Target_And_Runtime_From_Config; end if; -- Looking for the project file in predefined paths if the default -- project path has been initialized. if Env /= null and then Is_Initialized (Env.Env.Project_Path) then Find_Project (Env.Env.Project_Path, Root_Project_Path.Display_Full_Name, "", Pth); if Pth /= No_Path then Project_File := Create (+Get_Name_String (Pth)); end if; end if; if not Is_Regular_File (Project_File) then Trace (Me, "Load: " & Display_Full_Name (Root_Project_Path) & " is not a regular file"); Project_File := Create (Normalize_Pathname (Full_Name (Project_File) & Project_File_Extension, Resolve_Links => False)); if not Is_Regular_File (Project_File) then Trace (Me, "Load: " & Display_Full_Name (Project_File) & " is not a regular file"); if Errors /= null then Errors (Display_Full_Name (Root_Project_Path) & " is not a regular file"); end if; raise Invalid_Project; end if; end if; Tmp.Data := new Project_Tree_Data (Is_Aggregated => False); if Env = null then if Self.Data = null or else Self.Data.Env = null then Initialize (Tmp.Data.Env); else Tmp.Data.Env := Self.Data.Env; end if; else Tmp.Data.Env := Env; end if; -- Force a recomputation of the timestamp the next time Recompute_View -- is called. Tmp.Data.Timestamp := GNATCOLL.Utils.No_Time; Register_Specific_Attributes; Trace (Me, "Initial parsing to check the syntax"); Internal_Load (Tmp, Project_File, Errors, Report_Syntax_Errors => True, Project => Project, Packages_To_Check => Packages_To_Check, Recompute_View => False, Report_Missing_Dirs => Report_Missing_Dirs, Implicit_Project => False); GPR.Err.Initialize; -- Clear errors if Project = Empty_Project_Node then -- Reset the list of error messages, and keep current project -- unchanged if Self.Data = null then Self.Load_Empty_Project (Env => Tmp.Data.Env); end if; Free (Tmp.Data.View); Free (Tmp.Data); Trace (Me, "empty_node after parsing the tree"); raise Invalid_Project; end if; -- We know the project is syntactically correct, so we can go on with -- the processing (we can't reuse the previous parsing, because we need -- to Unload first. if Self.Data = null then Self.Data := Tmp.Data; else Project_Tree'Class (Self).Unload; Self.Data.Timestamp := GNATCOLL.Utils.No_Time; Self.Data.Env := Tmp.Data.Env; Free (Tmp.Data.View); Free (Tmp.Data); end if; Trace (Me, "Parsing again, now that we know the syntax is correct"); Internal_Load (Self, Project_File, Errors, Report_Syntax_Errors => False, -- already done above Project => Project, Packages_To_Check => Packages_To_Check, Recompute_View => Recompute_View, Report_Missing_Dirs => Report_Missing_Dirs, Implicit_Project => False); if Previous_Status = Default then Trace (Me, "Remove previous default project on disk, no longer used"); Delete (Previous_Project, Success); end if; end Load; --------------------- -- Set_Config_File -- --------------------- procedure Set_Config_File (Self : in out Project_Environment; Config_File : GNATCOLL.VFS.Virtual_File) is begin Self.Config_File := Config_File; end Set_Config_File; ------------------------------- -- Set_Automatic_Config_File -- ------------------------------- procedure Set_Automatic_Config_File (Self : in out Project_Environment; Autoconf : Boolean := True) is begin Self.Autoconf := Autoconf; end Set_Automatic_Config_File; -------------------- -- Add_Config_Dir -- -------------------- procedure Add_Config_Dir (Self : in out Project_Environment; Directory : GNATCOLL.VFS.Virtual_File) is pragma Unreferenced (Self); begin Name_Len := 0; Add_Str_To_Name_Buffer (Directory.Display_Full_Name); GPR.Conf.Add_Db_Switch_Arg (Name_Find); end Add_Config_Dir; ---------------- -- Initialize -- ---------------- procedure Initialize (Self : in out Project_Environment_Access; IDE_Mode : Boolean := False) is Path : String_Access; begin if Self = null then Self := new Project_Environment; end if; GPR.Tree.Initialize (Self.Env, Create_Flags (null)); GPR.Env.Initialize_Default_Project_Path (Self.Env.Project_Path, Target_Name => ""); GPR.Env.Get_Path (Self.Env.Project_Path, Path); Self.Predefined_Project_Path := new File_Array'(From_Path (+Path.all)); Self.IDE_Mode := IDE_Mode; end Initialize; ----------- -- Reset -- ----------- procedure Reset (Tree : in out Project_Tree'Class; Env : Project_Environment_Access) is begin if Tree.Data = null then Tree.Data := new Project_Tree_Data (Is_Aggregated => False); if Env = null then Initialize (Tree.Data.Env); else Tree.Data.Env := Env; end if; end if; if Tree.Data.Tree = null then Tree.Data.Tree := new Project_Node_Tree_Data; end if; GPR.Tree.Initialize (Tree.Data.Tree); if Tree.Data.View = null then Tree.Data.View := new GPR.Project_Tree_Data; end if; GPR.Initialize (Tree.Data.View); end Reset; ----------------------------- -- Invalidate_Gnatls_Cache -- ----------------------------- procedure Invalidate_Gnatls_Cache (Self : in out Project_Environment) is begin Free (Self.Gnatls); end Invalidate_Gnatls_Cache; ------------------------ -- Set_Default_Gnatls -- ------------------------ procedure Set_Default_Gnatls (Self : in out Project_Environment; Gnatls : String) is begin Free (Self.Default_Gnatls); Self.Default_Gnatls := new String'(Gnatls); end Set_Default_Gnatls; ---------------------------- -- Set_Target_And_Runtime -- ---------------------------- procedure Set_Target_And_Runtime (Self : in out Project_Environment; Target : String := ""; Runtime : String := "") is begin Free (Self.Forced_Target); Free (Self.Forced_Runtime); if Target /= "" then Self.Forced_Target := new String'(Target); end if; if Runtime /= "" then Self.Forced_Runtime := new String'(Runtime); end if; end Set_Target_And_Runtime; ---------------------------------------- -- Set_Target_And_Runtime_From_Config -- ---------------------------------------- procedure Set_Target_And_Runtime_From_Config (Self : in out Project_Environment) is Config_Project_Node : GPR.Project_Node_Id; Project_Node_Tree : GPR.Project_Node_Tree_Ref := new Project_Node_Tree_Data; Project_Tree : Project_Tree_Ref := new GPR.Project_Tree_Data (Is_Root_Tree => True); Config : Project_Id; Success : Boolean; function Get_Config_Attribute_Value (Config_File : Project_Id; Project_Tree : Project_Tree_Ref; Name : String; Index : String := ""; Pack : String := "") return String; -- Retruns the value of specified attribute from configuration project -- or an empty string if corresponding attribute is not found. -- Name, Index and Pack parameters are not case-sensitive. function Gnatls_From_CGPR (Runtime : String; Gcc : String) return String; -- Constructs call to gnatls based on attributes from configuration -- project. -------------------------------- -- Get_Config_Attribute_Value -- -------------------------------- function Get_Config_Attribute_Value (Config_File : Project_Id; Project_Tree : Project_Tree_Ref; Name : String; Index : String := ""; Pack : String := "") return String is Shared : constant Shared_Project_Tree_Data_Access := Project_Tree.Shared; Conf_Decl : Declarations; Conf_Attr_Id : Variable_Id; Conf_Attr : Variable; Conf_Array_Id : Array_Id; Conf_Array : Array_Data; Conf_Array_Elem_Id : Array_Element_Id; Conf_Array_Elem : Array_Element; Conf_Pack_Id : Package_Id; Conf_Pack : Package_Element; function "=" (L : Name_Id; R : String) return Boolean is (To_Lower (Get_Name_String (L)) = To_Lower (R)); begin if Config_File = GPR.No_Project then return ""; end if; Conf_Decl := Config_File.Decl; if Pack /= "" then Conf_Pack_Id := Conf_Decl.Packages; while Conf_Pack_Id /= No_Package loop Conf_Pack := Shared.Packages.Table (Conf_Pack_Id); exit when Conf_Pack.Name = Pack; Conf_Pack_Id := Conf_Pack.Next; end loop; if Conf_Pack_Id = No_Package then return ""; else Conf_Decl := Conf_Pack.Decl; end if; end if; if Index = "" then Conf_Attr_Id := Conf_Decl.Attributes; while Conf_Attr_Id /= GPR.No_Variable loop Conf_Attr := Shared.Variable_Elements.Table (Conf_Attr_Id); if not Conf_Attr.Value.Default then if Conf_Attr.Name = Name then if Conf_Attr.Value.Kind = Single then return Get_Name_String (Conf_Attr.Value.Value); end if; end if; end if; Conf_Attr_Id := Conf_Attr.Next; end loop; else Conf_Array_Id := Conf_Decl.Arrays; while Conf_Array_Id /= No_Array loop Conf_Array := Shared.Arrays.Table (Conf_Array_Id); if Conf_Array.Name = Name then Conf_Array_Elem_Id := Conf_Array.Value; while Conf_Array_Elem_Id /= No_Array_Element loop Conf_Array_Elem := Shared.Array_Elements.Table (Conf_Array_Elem_Id); if Conf_Array_Elem.Index = Index then return Get_Name_String (Conf_Array_Elem.Value.Value); end if; Conf_Array_Elem_Id := Conf_Array_Elem.Next; end loop; end if; Conf_Array_Id := Conf_Array.Next; end loop; end if; return ""; end Get_Config_Attribute_Value; ---------------------- -- Gnatls_From_CGPR -- ---------------------- function Gnatls_From_CGPR (Runtime : String; Gcc : String) return String is Idx : Integer; begin if Gcc = "" then return "gnatls -v" & (if Runtime = "" then "" else "--RTS=" & Runtime); else Idx := Index (Gcc, "gcc", Backward); if Idx > Gcc'First and then Idx = Gcc'Last - 2 then return Gcc (Gcc'First .. Idx - 1) & "gnatls -v" & (if Runtime = "" then "" else " --RTS=" & Runtime); end if; end if; return "gnatls -v"; end Gnatls_From_CGPR; begin Trace (Me, "Set_Target_And_Runtime_From_Config"); if Self.Config_File = GNATCOLL.VFS.No_File or else not Self.Config_File.Is_Regular_File then Trace (Me, "Config file not found"); return; end if; GPR.Snames.Initialize; GPR.Attr.Initialize; GPR.Tree.Initialize (Project_Node_Tree); GPR.Initialize (Project_Tree); GPR.Part.Parse (In_Tree => Project_Node_Tree, Project => Config_Project_Node, Project_File_Name => Self.Config_File.Display_Full_Name, Packages_To_Check => GPR.All_Packages, Is_Config_File => True, Env => Self.Env); if not Present (Config_Project_Node) then Trace (Me, "Cannot parse config project"); return; end if; Proc.Process_Project_Tree_Phase_1 (In_Tree => Project_Tree, Project => Config, Packages_To_Check => GPR.All_Packages, Success => Success, From_Project_Node => Config_Project_Node, From_Project_Node_Tree => Project_Node_Tree, Env => Self.Env, Reset_Tree => False, On_New_Tree_Loaded => null); if not Success then Trace (Me, "Cannot process config project"); return; end if; declare CGPR_Target : constant String := Get_Config_Attribute_Value (Config, Project_Tree, "target"); CGPR_Runtime : constant String := Get_Config_Attribute_Value (Config, Project_Tree, "Runtime_Dir", "ADA"); CGPR_GCC : constant String := Get_Config_Attribute_Value (Config, Project_Tree, "Driver", "ADA", "Compiler"); begin Free (Self.Forced_Target); Free (Self.Forced_Runtime); if CGPR_Target /= "" then Self.Forced_Target := new String'(CGPR_Target); end if; if CGPR_Runtime /= "" then Self.Forced_Runtime := new String'(CGPR_Runtime); end if; Trace (Me, CGPR_GCC); Trace (Me, Gnatls_From_CGPR (CGPR_Runtime, CGPR_GCC)); Self.Set_Default_Gnatls (Gnatls_From_CGPR (CGPR_Runtime, CGPR_GCC)); end; Free (Project_Tree); Free (Project_Node_Tree); end Set_Target_And_Runtime_From_Config; ------------------------------------ -- Set_Path_From_Gnatls_Attribute -- ------------------------------------ function Set_Path_From_Gnatls_Attribute (Project : Project_Id; Tree : Project_Tree'Class; Errors : Error_Report := null) return Boolean is P : Package_Id; Value : Variable_Value; GNAT_Version : GNAT.Strings.String_Access; Shared : constant Shared_Project_Tree_Data_Access := Tree.Data.View.Shared; Unset : constant String := ""; -- Should we read the 'target' attribute set in the .cgpr file -- possibly generated by gprconfig ? For native targets, it makes -- no sense, since we would try to execute "x86-window-gnatls" which -- does not exit. -- For cross-targets, this also seems useless: the target set in the -- .cgpr file is the one that was passed to gprconfig via --target, -- and therefore was set by the project manager and/or gnatcoll before -- hand. So it either comes from Set_Target_And_Runtime (first case -- below) or from the Target attribute in the user's project (second -- case below). Target_Value : constant Variable_Value := Value_Of (Get_String ("target"), Project.Decl.Attributes, Shared); Target : constant String := (if Tree.Data.Env.Forced_Target /= null then Tree.Data.Env.Forced_Target.all elsif Target_Value.Project = Project then Value_Of (Target_Value, Unset) else ""); N_Target : constant String := Normalize_Target_Name (Target); function Get_Value_Of_Runtime (Project : Project_Id) return String; -- Look for the value of Runtime attribute in given project or projects -- extended by it recursively. function Get_Value_Of_Runtime (Project : Project_Id) return String is Elem : constant Array_Element_Id := Value_Of (Get_String ("runtime"), Project.Decl.Arrays, Shared); begin if Elem = No_Array_Element then if Project.Extends = GPR.No_Project then return Value_Of (Nil_Variable_Value, Unset); else return Get_Value_Of_Runtime (Project.Extends); end if; else return Value_Of (Value_Of (Index => Get_String ("ada"), In_Array => Elem, Shared => Shared), Unset); end if; end Get_Value_Of_Runtime; Runtime : constant String := (if Tree.Data.Env.Forced_Runtime /= null then Tree.Data.Env.Forced_Runtime.all else Get_Value_Of_Runtime (Project)); function Default_Gnatls return String; -- Compute the default 'gnatls' command to spawn function Default_Gnatls return String is No_Prefix : Boolean := False; begin if Tree.Data.Env.Config_File.Is_Regular_File and then Tree.Data.Env.Default_Gnatls /= null then return Tree.Data.Env.Default_Gnatls.all; end if; for Tgt of Host_Targets_List loop if N_Target = Tgt then No_Prefix := True; exit; end if; end loop; if Runtime /= Unset or else Target /= Unset then return (if Target /= Unset and then not No_Prefix then Target & '-' else "") & "gnatls" & (if Runtime /= Unset then " --RTS=" & Runtime else ""); else return Tree.Data.Env.Default_Gnatls.all; end if; end Default_Gnatls; function Process_Gnatls (Gnatls : String) return Boolean; function Process_Gnatls (Gnatls : String) return Boolean is begin if Tree.Data.Env.Gnatls = null or else (Tree.Data.Env.Gnatls.all /= Gnatls and then Tree.Data.Env.Gnatls.all /= No_Gnatls) then Tree.Data.Env.Set_Path_From_Gnatls (Gnatls => Gnatls, GNAT_Version => GNAT_Version, Errors => Errors); Free (GNAT_Version); return True; end if; return False; end Process_Gnatls; begin P := Value_Of (Name_Ide, In_Packages => Project.Decl.Packages, Shared => Shared); if P = No_Package then Trace (Me, "No package IDE, no gnatlist attribute"); return Process_Gnatls (Default_Gnatls); else -- Do we have a gnatlist attribute ? Value := Value_Of (Get_String ("gnatlist"), Tree.Data.View.Shared.Packages.Table (P).Decl.Attributes, Shared); if Value = Nil_Variable_Value then Trace (Me, "No attribute IDE'gnatlist"); return Process_Gnatls (Default_Gnatls); else declare Gnatls : constant String := Get_Name_String (Value.Value); begin if Gnatls = "" then return Process_Gnatls (Default_Gnatls); else if Runtime /= Unset or else Target /= Unset then Trace (Me, "Error, IDE'Gnatlist attribute cannot be set" & " when Runtime or Target is also set"); return Process_Gnatls (Default_Gnatls); end if; return Process_Gnatls (Gnatls); end if; end; end if; end if; end Set_Path_From_Gnatls_Attribute; ------------------ -- Spawn_Gnatls -- ------------------ procedure Spawn_Gnatls (Self : Project_Environment; Fd : out Process_Descriptor_Access; Gnatls_Args : Argument_List_Access; Errors : Error_Report) is Gnatls_Path : constant Virtual_File := Locate_On_Path (+Gnatls_Args (Gnatls_Args'First).all); begin if Gnatls_Path = GNATCOLL.VFS.No_File then Trace (Me, "Could not locate exec " & Gnatls_Args (Gnatls_Args'First).all); if Errors /= null then Errors ("Could not locate exec " & Gnatls_Args (Gnatls_Args'First).all); end if; else Trace (Me, "Spawning " & (+Gnatls_Path.Full_Name)); if Self.TTY_Process_Descriptor_Disabled then Fd := new Process_Descriptor; else Fd := new TTY_Process_Descriptor; end if; Non_Blocking_Spawn (Fd.all, +Gnatls_Path.Full_Name, Gnatls_Args (Gnatls_Args'First + 1 .. Gnatls_Args'Last), Buffer_Size => 0, Err_To_Out => True); end if; end Spawn_Gnatls; ----------------- -- Gnatls_Host -- ----------------- function Gnatls_Host (Self : Project_Environment) return String is pragma Unreferenced (Self); begin return Local_Host; end Gnatls_Host; -------------------------- -- Set_Path_From_Gnatls -- -------------------------- procedure Set_Path_From_Gnatls (Self : in out Project_Environment; Gnatls : String; GNAT_Version : out GNAT.Strings.String_Access; Errors : Error_Report := null) is Gnatls_Args : Argument_List_Access := Argument_String_To_List (Gnatls & " -v"); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Process_Descriptor'Class, Process_Descriptor_Access); Success : Boolean := True; Fd : Process_Descriptor_Access; begin if Self.Default_Gnatls /= null and then Self.Default_Gnatls.all = No_Gnatls then Self.Gnatls := new String'(Self.Default_Gnatls.all); Trace (Me, "Gnatls should not be invoked"); return; end if; if Self.Gnatls /= null and then Self.Gnatls.all = Gnatls then Trace (Me, "Gnatls was already run with same arguments: " & Gnatls); return; end if; Free (Self.Gnatls); Self.Gnatls := new String'(Gnatls); Increase_Indent (Me, "Executing " & Gnatls & " -v"); begin Spawn_Gnatls (Project_Environment'Class (Self), Fd, Gnatls_Args, Errors); exception when others => Trace (Me, "Could not execute " & Gnatls_Args (1).all); if Errors /= null then Errors ("Could not execute " & Gnatls_Args (1).all); end if; Success := False; end; if not Success then Trace (Me, "Could not compute predefined paths"); if Errors /= null then Errors ("Could not compute predefined paths for this project."); Errors ("Subprojects might be incorrectly loaded, please make " & "sure they are in your ADA_PROJECT_PATH"); end if; Decrease_Indent (Me); return; end if; if Fd /= null then declare S : constant String := GNATCOLL.Utils.Get_Command_Output (Fd); begin Trace (Me, "Output of gnatls is " & S); if S = "" and Errors /= null then Errors ("The output from '" & Gnatls & "-v' is empty"); end if; Set_Path_From_Gnatls_Output (Self, Output => S, GNAT_Version => GNAT_Version, Host => Gnatls_Host (Project_Environment'Class (Self))); end; Unchecked_Free (Fd); end if; Free (Gnatls_Args); Decrease_Indent (Me); end Set_Path_From_Gnatls; --------------------------------- -- Set_Path_From_Gnatls_Output -- --------------------------------- procedure Set_Path_From_Gnatls_Output (Self : in out Project_Environment; Output : String; Host : String := GNATCOLL.VFS.Local_Host; GNAT_Version : out GNAT.Strings.String_Access) is type Path_Context is (None, Source_Path, Object_Path, Project_Path); Context : Path_Context := None; Current : GNATCOLL.VFS.File_Array_Access := new File_Array'(1 .. 0 => <>); Object_Path_Set : Boolean := False; procedure Add_Directory (S : String); -- Add S to the search path. -- If Source_Path is True, the source path is modified. -- Otherwise, the object path is modified. procedure Set_Context (New_Context : Path_Context); -- Change the context ------------------- -- Add_Directory -- ------------------- procedure Add_Directory (S : String) is Dir : Virtual_File; begin if S = "" then return; elsif S = "" then if not Object_Path_Set then -- Do not include "." in the default source/object paths: when -- the user is compiling, it would represent the object -- directory, when the user is searching file it would -- represent whatever the current directory is at that point, -- ... return; else Dir := Create_From_Base ("."); Ensure_Directory (Dir); Append (Current, Dir); end if; else Dir := To_Local (Create (+S, Host)); Append (Current, Dir); end if; end Add_Directory; ----------------- -- Set_Context -- ----------------- procedure Set_Context (New_Context : Path_Context) is begin case Context is when None => null; when Source_Path => Self.Set_Predefined_Source_Path (Current.all); when Object_Path => Object_Path_Set := True; Self.Set_Predefined_Object_Path (Current.all); when Project_Path => Self.Set_Predefined_Project_Path (Current.all); end case; if Active (Me) and then Context /= None then Trace (Me, "Set " & Context'Img & " from gnatls to:"); for J in Current'Range loop Trace (Me, " " & Current (J).Display_Full_Name); end loop; end if; Context := New_Context; Unchecked_Free (Current); if Context /= None then Current := new File_Array'(1 .. 0 => <>); end if; end Set_Context; F, L : Natural; begin F := Output'First; Skip_Blanks (Output, F); L := EOL (Output (F .. Output'Last)); declare S : constant String := Strip_CR (Output (F .. L - 1)); begin GNAT_Version := new String'(S (S'First + 7 .. S'Last)); Project_Environment'Class (Self).Set_GNAT_Version (GNAT_Version.all); end; F := L + 1; while F <= Output'Last loop L := EOL (Output (F .. Output'Last)); if GU.Starts_With (Output (F .. L - 1), "Source Search Path:") then Set_Context (Source_Path); elsif GU.Starts_With (Output (F .. L - 1), "Object Search Path:") then Set_Context (Object_Path); elsif GU.Starts_With (Output (F .. L - 1), "Project Search Path:") then Set_Context (Project_Path); elsif Context /= None then Add_Directory (Trim (Strip_CR (Output (F .. L - 1)), Ada.Strings.Left)); end if; F := L + 1; end loop; Set_Context (None); end Set_Path_From_Gnatls_Output; ------------------- -- Internal_Load -- ------------------- procedure Internal_Load (Tree : in out Project_Tree'Class; Root_Project_Path : GNATCOLL.VFS.Virtual_File; Errors : Projects.Error_Report; Report_Syntax_Errors : Boolean; Project : out Project_Node_Id; Packages_To_Check : GNAT.Strings.String_List_Access := All_Packs; Recompute_View : Boolean := True; Test_With_Missing_With : Boolean := True; Report_Missing_Dirs : Boolean := True; Implicit_Project : Boolean) is Block_Me : constant Block_Trace_Handle := Create (Me); procedure On_Error is new Mark_Project_Error (Tree); -- Any error while parsing the project marks it as incomplete, and -- prevents direct edition of the project. procedure Fail (S : String); -- Replaces Osint.Fail procedure Filter_Reload_Warnings (S : String); -- When loading a new project on top of an already loaded one, and both -- those projects have same name of external, but those externals -- correspond to different set of values, gprlib issues a warning. -- This warning is harmless and does not prevent the loading of the new -- project. However we can not clear the externals table since there are -- cases when we do want to store the values of all externals. -- So we just filter out such warnings. procedure Clean_Up_Node_Tree (Node_Tree : GPR.Project_Node_Tree_Ref; Tree : Project_Tree_Ref; Project_Node : Project_Node_Id; Project : Project_Id); -- Simple callback to free unused node trees that may be created for -- aggregated projects. ------------------------ -- Clean_Up_Node_Tree -- ------------------------ procedure Clean_Up_Node_Tree (Node_Tree : GPR.Project_Node_Tree_Ref; Tree : Project_Tree_Ref; Project_Node : Project_Node_Id; Project : Project_Id) is pragma Unreferenced (Tree, Project_Node, Project); Local_Node_Tree : GPR.Project_Node_Tree_Ref := Node_Tree; begin Free (Local_Node_Tree); end Clean_Up_Node_Tree; ---------------------------- -- Filter_Reload_Warnings -- ---------------------------- procedure Filter_Reload_Warnings (S : String) is Pattern : constant String := """ is illegal for typed string """; begin if Errors = null then Trace (Me, "calling output wrapper when Errors callback not set"); return; end if; if Index (S, Pattern) = 0 then Errors (S); end if; end Filter_Reload_Warnings; ---------- -- Fail -- ---------- procedure Fail (S : String) is begin if Report_Syntax_Errors and then Errors /= null then Errors (S); end if; end Fail; Predefined_Path : constant String := +To_Path (Predefined_Project_Path (Tree.Data.Env.all)); Errout_Handling : GPR.Part.Errout_Mode := GPR.Part.Always_Finalize; begin Traces.Assert (Me, Tree.Data /= null, "Tree data initialized"); Reset (Tree, Tree.Data.Env); Trace (Me, "project path is " & Predefined_Path); Initialize_Empty (Tree.Data.Env.Env.Project_Path); GPR.Env.Set_Path (Tree.Data.Env.Env.Project_Path, Predefined_Path); Project := Empty_Project_Node; -- Make sure errors are reinitialized before load GPR.Err.Initialize; if Test_With_Missing_With then Errout_Handling := GPR.Part.Never_Finalize; end if; if Errors = null then -- We do not want to loose the output in the wrapper if the callback -- is not specified. GPR.Output.Cancel_Special_Output; else if Tree.Data.Env.IDE_Mode then GPR.Output.Set_Special_Output (Filter_Reload_Warnings'Unrestricted_Access); else GPR.Output.Set_Special_Output (GPR.Output.Output_Proc (Errors)); end if; end if; GPR.Com.Fail := Fail'Unrestricted_Access; Tree.Data.Root := No_Project; Override_Flags (Tree.Data.Env.Env, Create_Flags (On_Error'Unrestricted_Access, Report_Missing_Dirs => not Report_Missing_Dirs, Ignore_Missing_With => Test_With_Missing_With)); GPR.Part.Parse (Tree.Data.Tree, Project, +Root_Project_Path.Full_Name, Packages_To_Check => Packages_To_Check, Errout_Handling => Errout_Handling, Store_Comments => True, Is_Config_File => False, Env => Tree.Data.Env.Env, Current_Directory => Get_Current_Dir, Implicit_Project => Implicit_Project); if not Active (Me_Aggregate_Support) and then Project /= Empty_Project_Node and then Project_Qualifier_Of (Project, Tree.Data.Tree) = GPR.Aggregate then Trace (Me, "Aggregate projects are not supported"); Fail ("Aggregate projects are not supported"); Project := Empty_Project_Node; GPR.Com.Fail := null; GPR.Output.Cancel_Special_Output; return; end if; if Project /= Empty_Project_Node and then Tree.Data.Tree.Incomplete_With then Trace (Me, "Could not find some with-ed projects"); -- Some "with" were found that could not be resolved. Check whether -- the user has specified a "gnatlist" switch. For this, we need to -- do phase1 of the processing (i.e. not look for sources). declare Success : Boolean; Tmp_Prj : Project_Id; Dummy : Boolean; begin Tree.Data.Projects.Clear; GPR.Proc.Process_Project_Tree_Phase_1 (In_Tree => Tree.Data.View, Project => Tmp_Prj, Packages_To_Check => Packages_To_Check, Success => Success, From_Project_Node => Project, From_Project_Node_Tree => Tree.Data.Tree, Env => Tree.Data.Env.Env, Reset_Tree => True, On_New_Tree_Loaded => Clean_Up_Node_Tree'Unrestricted_Access); if not Success or else Tmp_Prj = null then Trace (Me, "Processing phase 1 failed"); Project := Empty_Project_Node; else Trace (Me, "Looking for IDE'gnatlist attribute"); Dummy := Set_Path_From_Gnatls_Attribute (Tmp_Prj, Tree, Fail'Unrestricted_Access); end if; -- Reparse the tree so that errors are reported as usual -- (or not if the new project path solves the issue). Override_Flags (Tree.Data.Env.Env, Create_Flags (On_Error'Unrestricted_Access, Report_Missing_Dirs => Report_Missing_Dirs, Ignore_Missing_With => False)); Trace (Me, "Parsing project tree a second time"); Internal_Load (Tree => Tree, Root_Project_Path => Root_Project_Path, Errors => Errors, Report_Syntax_Errors => Report_Syntax_Errors, Project => Project, Recompute_View => Recompute_View, Packages_To_Check => Packages_To_Check, Test_With_Missing_With => False, Report_Missing_Dirs => Report_Missing_Dirs, Implicit_Project => Implicit_Project); GPR.Com.Fail := null; GPR.Output.Cancel_Special_Output; return; end; elsif Project = Empty_Project_Node and then Test_With_Missing_With then -- We had error, but we might be missing the one for missing withs. -- So we do a second parsing to make sure these error messages are -- there. Trace (Me, "Had error messages, reparsing to include missing withs"); Override_Flags (Tree.Data.Env.Env, Create_Flags (On_Error'Unrestricted_Access, Report_Missing_Dirs => Report_Missing_Dirs, Ignore_Missing_With => False)); Internal_Load (Tree => Tree, Root_Project_Path => Root_Project_Path, Errors => Errors, Report_Syntax_Errors => Report_Syntax_Errors, Project => Project, Recompute_View => Recompute_View, Packages_To_Check => Packages_To_Check, Test_With_Missing_With => False, Report_Missing_Dirs => Report_Missing_Dirs, Implicit_Project => Implicit_Project); GPR.Com.Fail := null; GPR.Output.Cancel_Special_Output; return; elsif Test_With_Missing_With then Trace (Me, "Project parsed with success"); -- We correctly parsed the project, but should finalize anyway if Report_Syntax_Errors then GPR.Err.Finalize; else GPR.Err.Initialize; end if; end if; -- Should we reprocess with a different predefined path ? -- We need to do a full reparse here (not just recompute the view), -- because changing gnatls might change the search path for projects, -- and thus the way the with statements are resolved. declare Success : Boolean; Tmp_Prj : Project_Id; Dummy : Boolean; begin Trace (Me, "Checking whether the gnatls attribute has changed"); -- Just clearing the projects htable is not enough, the memory will -- not be freed unless we set corresponding tree fields to null. -- Then finalize recognizes those project instances as useless -- and cleans them up. declare Cur : Project_Htables.Cursor := Tree.Data.Projects.First; begin while Cur /= Project_Htables.No_Element loop Project_Htables.Element (Cur).Data.Tree := null; Next (Cur); end loop; end; Tree.Data.Projects.Clear; GPR.Proc.Process_Project_Tree_Phase_1 (In_Tree => Tree.Data.View, Project => Tmp_Prj, Packages_To_Check => Packages_To_Check, Success => Success, From_Project_Node => Project, From_Project_Node_Tree => Tree.Data.Tree, Env => Tree.Data.Env.Env, Reset_Tree => True, On_New_Tree_Loaded => Clean_Up_Node_Tree'Unrestricted_Access); if Success and then Tmp_Prj /= null and then Set_Path_From_Gnatls_Attribute (Tmp_Prj, Tree, Fail'Unrestricted_Access) then Trace (Me, "load again with proper path"); Internal_Load (Tree => Tree, Root_Project_Path => Root_Project_Path, Errors => Errors, Report_Syntax_Errors => Report_Syntax_Errors, Project => Project, Recompute_View => Recompute_View, Packages_To_Check => Packages_To_Check, Test_With_Missing_With => False, Report_Missing_Dirs => Report_Missing_Dirs, Implicit_Project => Implicit_Project); GPR.Com.Fail := null; GPR.Output.Cancel_Special_Output; return; end if; end; Override_Flags (Tree.Data.Env.Env, Create_Flags (null)); if Project /= Empty_Project_Node then Tree.Data.Root := Tree.Instance_From_Node (Tree, Project); -- Create the project instances, so that we can use the -- project_iterator (otherwise Current cannot return a project_type). -- These instances, for now, will have now view associated Create_Project_Instances (Tree, Tree, With_View => False); Tree.Set_Status (From_File); if Report_Syntax_Errors then -- Some errors might come form GPR.Part.Parse but only from -- GPR.Proc.Process_Project_Tree_Phase_1, like undefined -- externals. We need to show them. GPR.Err.Finalize; end if; GPR.Com.Fail := null; GPR.Output.Cancel_Special_Output; -- For future recomputations of the view we want to keep the same -- flag over and over again. Tree.Data.Env.Report_Missing_Dirs := Report_Missing_Dirs; -- For future recomputations of view we also want to keep the list -- of packages to check, but in case it is not a predefined one, -- we need a hard copy, since users might free the list right after -- the loading. if Packages_To_Check in No_Packs | All_Packs then Tree.Data.Env.Packages_To_Check := Packages_To_Check; else Tree.Data.Env.Packages_To_Check := new String_List (Packages_To_Check'Range); for I in Packages_To_Check'Range loop Tree.Data.Env.Packages_To_Check (I) := new String'(Packages_To_Check (I).all); end loop; end if; if Recompute_View then Tree.Recompute_View (Errors => Errors); end if; end if; GPR.Com.Fail := null; GPR.Output.Cancel_Special_Output; exception when Invalid_Project => GPR.Com.Fail := null; GPR.Output.Cancel_Special_Output; raise; when E : others => Trace (Me, E); GPR.Com.Fail := null; GPR.Output.Cancel_Special_Output; raise; end Internal_Load; ---------------- -- Reset_View -- ---------------- procedure Reset_View (Tree : Project_Tree'Class) is begin if not Tree.Data.Is_Aggregated then Clean_Up (Tree.Data.Sources); Clean_Up (Tree.Data.Objects_Basename); end if; Tree.Data.Directories.Clear; Unchecked_Free (Tree.Data.Env.Scenario_Variables); Unchecked_Free (Tree.Data.Env.Untyped_Variables); end Reset_View; -------------------- -- Recompute_View -- -------------------- procedure Recompute_View (Self : in out Project_Tree; Errors : Projects.Error_Report := null) is Block_Me : constant Block_Trace_Handle := Create (Me); Actual_Config_File : Project_Node_Id := Empty_Project_Node; Actual_Config_File_Tree : GPR.Project_Node_Tree_Ref := null; -- The config file that was used (and possibly augmented by custom -- naming schemes set in Register_Default_Language_Extension) procedure Add_Default_GNAT_Naming_Scheme (Config_File : in out GPR.Project_Node_Id; Project_Tree : GPR.Project_Node_Tree_Ref); -- A hook that will create a new config file (in memory), used for -- Get_Or_Create_Configuration_File and Process_Project_And_Apply_Config -- and add the default GNAT naming scheme to it. Nothing is done if the -- config_file already exists, to avoid overriding what the user might -- have put in there. procedure Add_GPS_Naming_Schemes_To_Config_File (Config_File : in out Project_Node_Id; Project_Tree : GPR.Project_Node_Tree_Ref); -- Add the naming schemes defined in GPS's configuration files to the -- configuration file (.cgpr) used to parse the project. procedure On_Error is new Mark_Project_Error (Self); -- Any error while processing the project marks it as incomplete, and -- prevents direct edition of the project. procedure Initialize_Source_Records; -- Compute extra information for each source file, in particular whether -- it is a separate (as opposed to a body). This might require extra -- parsing of the source file in some cases. procedure On_New_Tree_Loaded (Node_Tree : GPR.Project_Node_Tree_Ref; Tree : Project_Tree_Ref; Project_Node : Project_Node_Id; Project : Project_Id); -- Creates project instances for given project tree. -- This is called once per aggregated project tree Undefined_Externals_Present : Boolean := False; procedure Catch_Undefined_Externals (S : String); -- Sets Undefined_Externals_Present to true if there is at least one -- error message about undefined externals when loading the project. -- This only works in IDE mode, since for other tools there is no way -- to change the Scenario Variables mid-loading and recompute view. ------------------------------------ -- Add_Default_GNAT_Naming_Scheme -- ------------------------------------ procedure Add_Default_GNAT_Naming_Scheme (Config_File : in out Project_Node_Id; Project_Tree : GPR.Project_Node_Tree_Ref) is Auto_Cgpr : constant String := "auto.cgpr"; procedure Create_Attribute (Name : Name_Id; Value : String; Index : String := ""; Pkg : Project_Node_Id := Empty_Project_Node); ---------------------- -- Create_Attribute -- ---------------------- procedure Create_Attribute (Name : Name_Id; Value : String; Index : String := ""; Pkg : Project_Node_Id := Empty_Project_Node) is Attr : Project_Node_Id; pragma Unreferenced (Attr); Expr : Name_Id := No_Name; Val : Name_Id := No_Name; Parent : Project_Node_Id := Config_File; begin if Index /= "" then Name_Len := Index'Length; Name_Buffer (1 .. Name_Len) := Index; Val := Name_Find; end if; if Pkg /= Empty_Project_Node then Parent := Pkg; end if; Name_Len := Value'Length; Name_Buffer (1 .. Name_Len) := Value; Expr := Name_Find; Attr := Create_Attribute (Tree => Project_Tree, Prj_Or_Pkg => Parent, Name => Name, Index_Name => Val, Kind => GPR.Single, Value => Create_Literal_String (Expr, Project_Tree)); end Create_Attribute; -- Local variables Name : Name_Id; Naming : Project_Node_Id; Compiler : Project_Node_Id; -- Start of processing for Add_Default_GNAT_Naming_Scheme begin if Config_File = Empty_Project_Node then -- Create a dummy config file if none was found Name_Len := Auto_Cgpr'Length; Name_Buffer (1 .. Name_Len) := Auto_Cgpr; Name := Name_Find; -- An invalid project name to avoid conflicts with -- user-created ones. Name_Len := 5; Name_Buffer (1 .. Name_Len) := "_auto"; Config_File := Create_Project (In_Tree => Project_Tree, Name => Name_Find, Full_Path => Path_Name_Type (Name), Is_Config_File => True); -- Setup library support Create_Attribute (Name_Library_Support, "full"); Create_Attribute (Name_Library_Auto_Init_Supported, "true"); -- Declare an empty target Create_Attribute (Name_Target, ""); -- Setup Ada support (Ada is the default language here, since this -- is only called when no config file existed initially, i.e. for -- gnatmake). Create_Attribute (Name_Default_Language, "ada"); Compiler := Create_Package (Project_Tree, Config_File, "compiler"); Create_Attribute (Name_Driver, "gcc", "ada", Pkg => Compiler); Create_Attribute (Name_Language_Kind, "unit_based", "ada", Pkg => Compiler); Create_Attribute (Name_Dependency_Kind, "ALI_File", "ada", Pkg => Compiler); Naming := Create_Package (Project_Tree, Config_File, "naming"); Create_Attribute (Name_Spec_Suffix, ".ads", "ada", Pkg => Naming); Create_Attribute (Name_Body_Suffix, ".adb", "ada", Pkg => Naming); Create_Attribute (Name_Spec_Suffix, ".h", "c", Pkg => Naming); Create_Attribute (Name_Body_Suffix, ".c", "c", Pkg => Naming); Create_Attribute (Name_Spec_Suffix, ".hh", "c++", Pkg => Naming); Create_Attribute (Name_Body_Suffix, ".cpp", "c++", Pkg => Naming); Create_Attribute (Name_Dot_Replacement, "-", Pkg => Naming); Create_Attribute (Name_Casing, "lowercase", Pkg => Naming); end if; end Add_Default_GNAT_Naming_Scheme; ------------------------------------------- -- Add_GPS_Naming_Schemes_To_Config_File -- ------------------------------------------- procedure Add_GPS_Naming_Schemes_To_Config_File (Config_File : in out Project_Node_Id; Project_Tree : GPR.Project_Node_Tree_Ref) is NS : Naming_Scheme_Access := Self.Data.Env.Naming_Schemes; Attr : Project_Node_Id; Spec_Suffix, Body_Suffix, Obj_Suffix : Name_Id; Naming_Pkg, Compiler_Pkg : Project_Node_Id; pragma Unreferenced (Attr); begin if Config_File = Empty_Project_Node then -- Create a dummy config file if none was found. In that case we -- need to provide the Ada naming scheme as well Trace (Me, "Creating dummy configuration file"); Add_Default_GNAT_Naming_Scheme (Config_File, Project_Tree); -- Pretend we support shared and static libs. Since we are not -- trying to build anyway, this isn't dangerous, and allows -- loading some libraries projects which otherwise we could not -- load. Attr := Create_Attribute (Tree => Project_Tree, Prj_Or_Pkg => Config_File, Name => Get_String ("library_support"), Kind => Single, Value => Create_Literal_String (Tree => Project_Tree, Str => Get_String ("full"))); end if; Spec_Suffix := Get_String ("spec_suffix"); Body_Suffix := Get_String ("body_suffix"); Obj_Suffix := Get_String ("object_file_suffix"); Naming_Pkg := Create_Package (Tree => Project_Tree, Project => Config_File, Pkg => "naming"); Compiler_Pkg := Create_Package (Tree => Project_Tree, Project => Config_File, Pkg => "compiler"); while NS /= null loop if NS.Default_Spec_Suffix.all /= Dummy_Suffix then Attr := Create_Attribute (Tree => Project_Tree, Prj_Or_Pkg => Naming_Pkg, Kind => Single, Name => Spec_Suffix, Index_Name => Get_String (NS.Language.all), Value => Create_Literal_String (Tree => Project_Tree, Str => Get_String (NS.Default_Spec_Suffix.all))); end if; if NS.Default_Body_Suffix.all /= Dummy_Suffix then Attr := Create_Attribute (Tree => Project_Tree, Prj_Or_Pkg => Naming_Pkg, Kind => Single, Name => Body_Suffix, Index_Name => Get_String (NS.Language.all), Value => Create_Literal_String (Tree => Project_Tree, Str => Get_String (NS.Default_Body_Suffix.all))); end if; if NS.Obj_Suffix /= null then Attr := Create_Attribute (Tree => Project_Tree, Prj_Or_Pkg => Compiler_Pkg, Kind => Single, Name => Obj_Suffix, Index_Name => Get_String (NS.Language.all), Value => Create_Literal_String (Tree => Project_Tree, Str => Get_String (NS.Obj_Suffix.all))); end if; NS := NS.Next; end loop; Actual_Config_File := Config_File; Actual_Config_File_Tree := Project_Tree; end Add_GPS_Naming_Schemes_To_Config_File; ------------------------------- -- Initialize_Source_Records -- ------------------------------- procedure Initialize_Source_Records is procedure For_Sources (Project : Project_Id; Tree : Project_Tree_Ref; With_State : in out Integer); ----------------- -- For_Sources -- ----------------- procedure For_Sources (Project : Project_Id; Tree : Project_Tree_Ref; With_State : in out Integer) is pragma Unreferenced (With_State); Iter : Source_Iterator := For_Each_Source (In_Tree => Tree, Project => Project); Src : GPR.Source_Id; begin loop Src := Element (Iter); exit when Src = No_Source; -- ??? Calling Initialize_Source_Record computes additional -- information that we do not need at the moment, at the cost -- of a few system calls per source file. So instead we just -- duplicate the part that computes whether we have a separate -- unit. if False then GPR.Util.Initialize_Source_Record (Src); else if Src.Language.Config.Kind = Unit_Based and then Src.Kind = Impl and then GPR.Util.Is_Subunit (Src) then Src.Kind := Sep; end if; end if; Next (Iter); end loop; end For_Sources; procedure For_Projects_Imported is new For_Every_Project_Imported (Integer, For_Sources); State : Integer := 0; begin For_Projects_Imported (By => Self.Root_Project.Data.View, Tree => Self.Data.View, With_State => State, Include_Aggregated => True, Imported_First => False); end Initialize_Source_Records; View : Project_Id; Automatically_Generated : Boolean; Config_File_Path : String_Access; Flags : Processing_Flags; Iter : Inner_Project_Iterator; Timestamp : Time; procedure On_New_Tree_Loaded (Node_Tree : GPR.Project_Node_Tree_Ref; Tree : Project_Tree_Ref; Project_Node : Project_Node_Id; Project : Project_Id) is pragma Unreferenced (Project); Path : constant Virtual_File := Create (+Get_String (Path_Name_Of (Project_Node, Node_Tree))); T : Project_Tree'Class := Self; -- copy the tag of self P : Project_Type; C : constant Project_Htables.Cursor := Self.Data.Projects.Find (Path); begin Trace (Me, "Loaded an aggregated tree"); -- Recomputing the view might impact which aggregated projects are -- seen, so we need to create new project trees as needed. if Has_Element (C) and then Element (C).Data.Tree /= null then P := Element (C); if P.Data.Node /= Project_Node then -- The only way we can end up here is if the given aggregated -- tree is in fact a subtree of a previously processed -- aggregated tree. This means that we had already created all -- corresponding project instances and have nothing to do. return; end if; P.Data.Tree.View := Tree; T.Data := P.Data.Tree; -- temporary else T.Data := new Project_Tree_Data' (Is_Aggregated => True, Env => Self.Data.Env, Tree => Node_Tree, View => Tree, Status => Self.Data.Status, Timestamp => Self.Data.Timestamp, others => <>); -- T.Data.Tree should be set before the instances can be created T.Data.Root := T.Instance_From_Node (Self, Project_Node); end if; Create_Project_Instances (T, Tree_For_Map => Self, With_View => False); end On_New_Tree_Loaded; ------------------------------- -- Catch_Undefined_Externals -- ------------------------------- procedure Catch_Undefined_Externals (S : String) is Pattern : constant String := "undefined external reference"; begin if Errors = null then Trace (Me, "calling output wrapper when Errors callback not set"); return; end if; if Index (S, Pattern) /= 0 then Undefined_Externals_Present := True; end if; Errors (S); end Catch_Undefined_Externals; Sources_Count : constant Source_File_Index := GPR.Sinput.Source_File_Last; begin if Self.Data.Env.IDE_Mode then GPR.Output.Set_Special_Output (Catch_Undefined_Externals'Unrestricted_Access); else GPR.Output.Set_Special_Output (GPR.Output.Output_Proc (Errors)); end if; -- The views stored in the projects are no longer valid, we should make -- sure they are not called. declare C : Project_Htables.Cursor := Self.Data.Projects.First; begin while Has_Element (C) loop Element (C).Data.View := GPR.No_Project; Next (C); end loop; end; Reset_View (Self); GPR.Initialize (Self.Data.View); Opt.Follow_Links_For_Files := not Self.Data.Env.Trusted_Mode; Opt.Follow_Links_For_Dirs := not Self.Data.Env.Trusted_Mode; begin Flags := Create_Flags (On_Error'Unrestricted_Access, Require_Sources => False, Report_Missing_Dirs => Self.Data.Env.Report_Missing_Dirs); -- Make sure errors are reinitialized before load GPR.Err.Initialize; Override_Flags (Self.Data.Env.Env, Flags); Trace (Me, "Configuration file is '" & Self.Data.Env.Config_File.Display_Full_Name & "' autoconf=" & Self.Data.Env.Autoconf'Img & " for target " & Self.Root_Project.Get_Target); -- Get_Target only returns a non-empty string when -- Set_Target_And_Runtime was called first; otherwise we depend on -- the project manager to extract target and runtime information -- from project attributes if Self.Root_Project.Data.Tree.Env.Forced_Runtime /= null then Set_Runtime_For (Get_String ("ada"), Self.Root_Project.Data.Tree.Env.Forced_Runtime.all); end if; Process_Project_And_Apply_Config (Main_Project => View, User_Project_Node => Self.Root_Project.Data.Node, Config_File_Name => Self.Data.Env.Config_File.Display_Full_Name, Autoconf_Specified => Self.Data.Env.Autoconf, Project_Tree => Self.Data.View, Project_Node_Tree => Self.Data.Tree, Packages_To_Check => Self.Data.Env.Packages_To_Check, Target_Name => Self.Root_Project.Get_Target, Allow_Automatic_Generation => Self.Data.Env.Autoconf, Automatically_Generated => Automatically_Generated, Config_File_Path => Config_File_Path, Env => Self.Data.Env.Env, Normalized_Hostname => "", On_Load_Config => Add_GPS_Naming_Schemes_To_Config_File'Unrestricted_Access, On_New_Tree_Loaded => On_New_Tree_Loaded'Unrestricted_Access); Free (Config_File_Path); -- Should we reprocess with a different predefined path ? -- A similar test has already been done in Internal_Load, which -- ensures we are resolving the with clauses correctly and not -- looking for source file with the wrong path. -- But we need to do this test again, in case the user has changed -- the scenario variables and they influence which gnatls command is -- run. Unfortunately, this mean we might have spent time looking -- for incorrect sources above. -- ??? It might be simpler to hide the Recompute_View altogether and -- force users to reload the project systematically (this would not -- change performance most likely) Trace (Me, "Checking whether the gnatls attribute has changed"); if View /= GPR.No_Project and then Set_Path_From_Gnatls_Attribute (View, Self, Errors) then Trace (Me, "recompute view a second time with proper path"); Reset_View (Self); GPR.Initialize (Self.Data.View); Process_Project_And_Apply_Config (Main_Project => View, User_Project_Node => Self.Root_Project.Data.Node, Config_File_Name => Self.Data.Env.Config_File.Display_Full_Name, Autoconf_Specified => Self.Data.Env.Autoconf, Project_Tree => Self.Data.View, Project_Node_Tree => Self.Data.Tree, Packages_To_Check => Self.Data.Env.Packages_To_Check, Allow_Automatic_Generation => Self.Data.Env.Autoconf, Automatically_Generated => Automatically_Generated, Config_File_Path => Config_File_Path, Env => Self.Data.Env.Env, Normalized_Hostname => "", On_Load_Config => Add_GPS_Naming_Schemes_To_Config_File'Unrestricted_Access, On_New_Tree_Loaded => On_New_Tree_Loaded'Unrestricted_Access); Free (Config_File_Path); end if; Override_Flags (Self.Data.Env.Env, Create_Flags (null)); exception when E : Invalid_Config => Trace (Me, Exception_Message (E)); -- not the exception itself if Errors /= null then Errors (Exception_Message (E)); end if; Override_Flags (Self.Data.Env.Env, Create_Flags (null)); -- Error message was already reported via GPR.Err null; end; -- Backward compatibility: load the project even if there was a fatal -- error. However, the view might be partial... -- if View = null then -- raise Invalid_Project; -- end if; Trace (Me, "View has been recomputed"); -- Now that we have the view, we can create the project instances if View = GPR.No_Project then -- There was an error, but we still want to manipulate that project Self.Data.Root.Data.View := Get_View (Self.Data.View, Path => GPR.Tree.Path_Name_Of (Self.Data.Root.Data.Node, Self.Data.Tree)); else Self.Data.Root.Data.View := View; end if; Create_Project_Instances (Self, Self, With_View => True); -- To get scenario variables from aggregated projects we first need -- all to fully parse all project trees and create instances of all -- projects. Compute_Scenario_Variables (Self.Data, Errors => Errors); Parse_Source_Files (Self); Initialize_Source_Records; -- If the timestamp have not been computed yet (ie we are loading a new -- project), do it now. -- We cannot simply use Clock here, since this returns local time, -- and the file timestamps will be returned in GMT, therefore we -- won't be able to compare. if Self.Data.Timestamp = GNATCOLL.Utils.No_Time and then Self.Data.Status = From_File then Iter := Start (Self.Root_Project); while Current (Iter) /= No_Project loop Timestamp := File_Time_Stamp (Project_Path (Current (Iter))); if Timestamp > Self.Data.Timestamp then Self.Data.Timestamp := Timestamp; end if; Next (Iter); end loop; end if; -- ??? Should not be needed since all errors are reported through the -- callback already. This avoids duplicate error messages in the console GPR.Err.Finalize; GPR.Output.Cancel_Special_Output; if Undefined_Externals_Present then Errors ("Some externals are undefined, project may be loaded incompletely" & ASCII.LF); Errors ("Set values of corresponding externals and reload the project" & ASCII.LF); end if; GPR.Sinput.Source_File_Trim (Sources_Count); -- Save the config file that was used to disk, if needed. This will -- be used when spawning other project-aware tools, since it might -- include extra naming schemes coming from calls to -- Register_Default_Language_Extension. if Self.Data.Env.Save_Config_File /= null and then Actual_Config_File /= Empty_Project_Node and then Self.Status = From_File then declare F : Ada.Text_IO.File_Type; type File_Pretty_Printer is new Pretty_Printer with null record; overriding procedure Put (Self : in out File_Pretty_Printer; C : Character); overriding procedure Put (Self : in out File_Pretty_Printer; C : Character) is pragma Unreferenced (Self); begin Put (F, C); end Put; P : File_Pretty_Printer; Gpsauto : Virtual_File; Dir : Virtual_File := Self.Root_Project.Object_Dir; begin if Dir = GNATCOLL.VFS.No_File then Dir := Create (Self.Root_Project.Project_Path.Dir_Name); end if; Gpsauto := Create_From_Dir (Dir => Dir, Base_Name => +Self.Data.Env.Save_Config_File.all); Trace (Me, "Saving config file to " & Gpsauto.Display_Full_Name); Ada.Text_IO.Create (F, Out_File, Gpsauto.Display_Full_Name); Put (Self => P, Project => Actual_Config_File, In_Tree => Actual_Config_File_Tree); Close (F); exception when Ada.Text_IO.Name_Error | Ada.Text_IO.Use_Error => Trace (Me, "Could not save config file"); end; end if; exception -- We can get an unexpected exception (actually Directory_Error) if the -- project file's path is invalid, for instance because it was -- modified by the user. when Invalid_Project => Trace (Me, "Could not compute project view"); GPR.Err.Finalize; GPR.Output.Cancel_Special_Output; raise; when E : others => Trace (Me, E); GPR.Err.Finalize; GPR.Output.Cancel_Special_Output; end Recompute_View; ------------------------ -- Instance_From_Node -- ------------------------ function Instance_From_Node (Self : Project_Tree'Class; Tree_For_Map : Project_Tree'Class; Node : Project_Node_Id) return Project_Type is Path : constant Virtual_File := Create (+Get_String (GPR.Tree.Path_Name_Of (Node, Self.Data.Tree))); Data : Project_Data_Access; P : Project_Type; C : constant Project_Htables.Cursor := Tree_For_Map.Data.Projects.Find (Path); begin if not Has_Element (C) then Data := Tree_For_Map.Data_Factory; Data.Tree := Self.Data; Data.Tree_For_Map := Tree_For_Map.Data; Data.Node := Node; P := Project_Type'(Ada.Finalization.Controlled with Data => Data); Tree_For_Map.Data.Projects.Include (Path, P); return P; else return Element (C); end if; end Instance_From_Node; ------------------------------ -- Create_Project_Instances -- ------------------------------ procedure Create_Project_Instances (Self : Project_Tree'Class; Tree_For_Map : Project_Tree'Class; With_View : Boolean) is procedure Do_Project (Proj : Project_Id; Tree : Project_Tree_Ref; S : in out Integer); procedure Do_Project2 (T : GPR.Project_Node_Tree_Ref; P : Project_Node_Id); ---------------- -- Do_Project -- ---------------- procedure Do_Project (Proj : Project_Id; Tree : Project_Tree_Ref; S : in out Integer) is pragma Unreferenced (S); -- , Tree); Iter : Project_Htables.Cursor; P : Project_Type; Path : Virtual_File; begin if not Proj.Virtual then Path := Create (+Get_String (Proj.Path.Display_Name)); Iter := Tree_For_Map.Data.Projects.Find (Path); if Has_Element (Iter) then P := Element (Iter); Reset_View (P.Data.all); -- For a given project, it does not matter much whether we are -- seeing the view from one aggregated project or another. But -- we must ensure that the project_id matches the view from the -- tree, otherwise the project will not be found by the prj* -- packages. if P.Data.Tree.View = null or else P.Data.Tree.View = Tree then P.Data.View := Proj; P.Data.Tree.View := Tree; -- must match Proj end if; elsif Active (Me) then Assert (Me, False, "Create_Project_Instances must be called" & " to create project_type for " & Path.Display_Full_Name); end if; end if; end Do_Project; ----------------- -- Do_Project2 -- ----------------- procedure Do_Project2 (T : GPR.Project_Node_Tree_Ref; P : Project_Node_Id) is pragma Unreferenced (T); Proj : Project_Type; begin Proj := Instance_From_Node (Self, Tree_For_Map, Node => P); Reset_View (Proj.Data.all); end Do_Project2; procedure For_All_Projects is new For_Every_Project_Imported (Integer, Do_Project); S : Integer := 0; begin if With_View then Assert (Me, Self.Data.Root.Data.View /= null, "Create_Project_Instances: Project not parsed"); For_All_Projects (Self.Data.Root.Data.View, Self.Data.View, S, Include_Aggregated => True); else For_Each_Project_Node (Self.Data.Tree, Self.Data.Root.Data.Node, Do_Project2'Unrestricted_Access); end if; end Create_Project_Instances; ------------------------ -- Load_Empty_Project -- ------------------------ procedure Load_Empty_Project (Self : in out Project_Tree; Env : Project_Environment_Access := null; Name : String := "empty"; Recompute_View : Boolean := True) is D : constant Filesystem_String := Name_As_Directory (Get_Current_Dir) & (+Name) & Project_File_Extension; Node : Project_Node_Id; begin Trace (Me, "Loading empty project"); Project_Tree'Class (Self).Unload; Reset (Self, Env); Node := GPR.Tree.Create_Project (In_Tree => Self.Data.Tree, Name => Get_String (Name), Full_Path => Path_Name_Type (Get_String (+D)), Is_Config_File => False); Self.Data.Root := Self.Instance_From_Node (Self, Node); Self.Set_Status (Empty); -- No language known for empty project Self.Data.Root.Set_Attribute (Languages_Attribute, (1 .. 0 => null)); Self.Data.Root.Data.Modified := False; Create_Project_Instances (Self, Self, With_View => False); if Recompute_View then Project_Tree'Class (Self).Recompute_View; end if; end Load_Empty_Project; --------------------------- -- Load_Implicit_Project -- --------------------------- procedure Load_Implicit_Project (Self : in out Project_Tree; Env : Project_Environment_Access := null; Recompute_View : Boolean := True) is Project_File : Virtual_File; Gprbuild_Path : Filesystem_String_Access; Project : Project_Node_Id; Implicit_Project_File_Path : constant String := "share" & Directory_Separator & "gpr" & Directory_Separator & '_' & Default_Project_File_Name; begin Trace (Me, "Loading implicit project "); Project_Tree'Class (Self).Unload; Reset (Self, Env); Gprbuild_Path := Locate_Exec_On_Path ("gprbuild"); if Gprbuild_Path = null then Trace (Me, "Gprbuild not found on path"); return; end if; Project_File := Get_Parent (Create (Dir_Name (Gprbuild_Path.all))); Project_File := Join (Project_File, +Implicit_Project_File_Path); Free (Gprbuild_Path); if not Project_File.Is_Regular_File then Trace (Me, "_default.gpr not found in expected location"); return; end if; Trace (Me, "Implicit project found " & Project_File.Display_Full_Name); Internal_Load (Self, Project_File, null, Report_Syntax_Errors => True, -- _default.gpr is safe Project => Project, Packages_To_Check => No_Packs, Recompute_View => Recompute_View, Implicit_Project => True); if Project = Empty_Project_Node then Trace (Me, "Cannot load implicit project"); return; end if; end Load_Implicit_Project; ------------------------ -- Parse_Source_Files -- ------------------------ procedure Parse_Source_Files (Self : in out Project_Tree) is Block_Me : constant Block_Trace_Handle := Create (Me); procedure Register_Directory (Directory : Filesystem_String); -- Register Directory as belonging to Project. -- The parent directories are also registered. ------------------------ -- Register_Directory -- ------------------------ procedure Register_Directory (Directory : Filesystem_String) is Dir : constant Filesystem_String := Name_As_Directory (Directory); Last : Integer := Dir'Last - 1; Curs : Directory_Statuses.Cursor; begin Self.Data.Directories.Include (Dir, Direct); loop while Last >= Dir'First and then Dir (Last) /= Directory_Separator and then Dir (Last) /= '/' loop Last := Last - 1; end loop; Last := Last - 1; exit when Last <= Dir'First; -- Register the name with a trailing directory separator Curs := Self.Data.Directories.Find (Dir (Dir'First .. Last + 1)); if not Has_Element (Curs) or else Element (Curs) /= Direct then Self.Data.Directories.Include (Dir (Dir'First .. Last + 1), As_Parent); end if; end loop; end Register_Directory; use Virtual_File_List; Gnatls : constant String := Self.Root_Project.Attribute_Value (Gnatlist_Attribute); Iter : Project_Iterator; Sources : String_List_Id; P : Project_Type; Source_Iter : Source_Iterator; Source : Source_Id; Source_File_List : Virtual_File_List.List; Tree_For_Map : constant Project_Tree_Data_Access := Self.Data.Root.Data.Tree_For_Map; begin Tree_For_Map.Objects_Basename.Clear; Iter := Self.Root_Project.Start (Recursive => True); loop P := Current (Iter); exit when P = No_Project; declare Ls : constant String := P.Attribute_Value (Gnatlist_Attribute); begin if Ls /= "" and then Ls /= Gnatls then -- We do not want to mark the project as incomplete for this -- warning, so we do not need to pass an actual Error_Handler GPR.Err.Error_Msg (Flags => Create_Flags (null), Msg => "?the project attribute IDE.gnatlist doesn't have" & " the same value as in the root project." & " The value """ & Gnatls & """ will be used", Project => Get_View (P)); end if; end; -- Reset the list of source files for this project. We must not -- Free it, since it is now stored in the previous project's instance Source_File_List := Virtual_File_List.Empty_List; -- Add the directories Sources := Get_View (P).Source_Dirs; while Sources /= Nil_String loop Register_Directory (+Get_String (String_Elements (Self.Data)(Sources).Value)); Sources := String_Elements (Self.Data)(Sources).Next; end loop; Register_Directory (+Get_String (Get_View (P).Object_Directory.Name)); Register_Directory (+Get_String (Get_View (P).Exec_Directory.Name)); -- Add the sources that are already in the project. -- Convert the names to UTF8 for proper handling in GPS Source_Iter := For_Each_Source (P.Data.Tree.View, Get_View (P), Locally_Removed => False); loop Source := Element (Source_Iter); exit when Source = No_Source; -- Get the absolute path name for this source Get_Name_String (Source.Path.Display_Name); declare File : constant Virtual_File := Create (+Name_Buffer (1 .. Name_Len)); begin if Self.Data.Root.Is_Aggregate_Project then -- If we have duplicates, create lists Include_File (Tree_For_Map.Sources, Base_Name (File), (P, File, Source.Language.Name, Source, null)); else -- No point in all the checks for regular project. Tree_For_Map.Sources.Include (Base_Name (File), (P, File, Source.Language.Name, Source, null)); end if; if Source.Object /= GPR.No_File and then Source.Language /= null -- and then Source.Language.Config.Object_File_Suffix /= -- Name_Op_Subtract ???? and then Get_String (Source.Language.Config.Object_File_Suffix) /= "-" then declare Base : constant Filesystem_String := Base_Name (Filesystem_String (Get_String (Source.Object)), ".o"); Base_Last : Natural := Base'Last; begin -- In GPS, users might define ada-based languages -- when they have local variations. In this case, -- they are likely to define the object suffix as -- ".ali", which we need to ignore as well. if Ends_With (String (Base), ".ali") then Base_Last := Base_Last - 4; end if; -- We know the actual object file will be in either -- P or one of its extending projects. We can't -- compute this information now though, because the -- sources might not have been compiled. So the final -- computation is done directly in Library_Files. if Source.Index = 0 then -- ??? What if we have a non-aggregate root, that -- imports a library aggregate project ? -- if Is_Aggregate_Project (Self.Data.Root) then Include_File (Tree_For_Map.Objects_Basename, Base (Base'First .. Base_Last), (P, File, Source.Language.Name, Source, null)); -- else -- -- No point in all the checks for regular -- -- project. -- -- Tree_For_Map.Objects_Basename.Include -- (Base (Base'First .. Base_Last), -- (P, File, Source.Language.Name, Source, -- null)); -- end if; else -- if Is_Aggregate_Project (Self.Data.Root) then Include_File (Tree_For_Map.Objects_Basename, Base (Base'First .. Base_Last) & "~" & (+Image (Integer (Source.Index), Min_Width => 0)), (P, File, Source.Language.Name, Source, null)); -- else -- -- No point in all the checks for regular -- -- project. -- -- Tree_For_Map.Objects_Basename.Include -- (Base (Base'First .. Base_Last) & "~" -- & (+Image -- (Integer (Source.Index), -- Min_Width => 0)), -- (P, File, Source.Language.Name, Source, -- null)); -- end if; end if; end; end if; -- The project manager duplicates files that contain several -- units. Only add them once in the project sources -- (and thus only when the Index is 0 (single unit) or 1 -- (first of multiple units). -- For source-based languages, we allow duplicate sources if Source.Unit = null or else Source.Index <= 1 then Prepend (Source_File_List, File); end if; end; Next (Source_Iter); end loop; -- Register the sources in our own caches declare Count : constant Ada.Containers.Count_Type := Virtual_File_List.Length (Source_File_List); Files : constant File_Array_Access := new File_Array (1 .. Natural (Count)); Current : Virtual_File_List.Cursor := First (Source_File_List); J : Natural := Files'First; begin while Has_Element (Current) loop -- ??? Create new virtual files to work around compiler bug. -- The ideal would have been to write: -- Files (J) := Element (Current) -- in order to avoid memory reallocations. Files (J) := Create (Element (Current).Full_Name); Next (Current); J := J + 1; end loop; Unchecked_Free (P.Data.Files); P.Data.Files := Files; if P.Data.Base_Name_To_Full_Path = null then P.Data.Base_Name_To_Full_Path := new Basename_To_Info_Cache.Map; else P.Data.Base_Name_To_Full_Path.Clear; end if; for F of P.Data.Files.all loop P.Data.Base_Name_To_Full_Path.Include (String (F.Base_Name), F); end loop; end; Next (Iter); end loop; end Parse_Source_Files; ------------ -- Unload -- ------------ procedure Unload (Self : in out Project_Tree) is Iter : Project_Htables.Cursor; Data : Project_Data_Access; begin if Self.Data = null then return; end if; Iter := Self.Data.Projects.First; -- Since we are going to free the tree, removing any reference to it in -- the projects that the user might keep around while Has_Element (Iter) loop Data := Element (Iter).Data; if Data.Tree.Tree /= Self.Data.Tree then Free (Data.Tree.Tree); end if; Data.Tree := null; Reset_View (Data.all); Data.Node := Empty_Project_Node; Next (Iter); end loop; if Self.Data.View /= null then Reset (Self.Data.View); end if; Self.Data.Root := No_Project; GPR.Tree_Private_Part.Projects_Htable.Reset (Self.Data.Tree.Projects_HT); Sinput.Clear_Source_File_Table; Sinput.Reset_First; -- Reset the scenario variables. -- The issue is that a given variable might currently have a value, and -- then be used in another project where that value is now illegal. -- Do not reset if we have an empty project, since otherwise we lose the -- values set from the command line -- ??? Don't reset after all, this is too tricky to get right, and might -- be plain wrong in fact. -- if Self.Data.Status /= Empty then -- GPR.Ext.Reset (Self.Data.Tree); -- end if; Reset_View (Self); -- Free all projects. This will decrease the refcounting for their data -- and possibly free the memory Self.Data.Projects.Clear; -- Do not reset the tree node, since it also contains the environment -- variables, which we want to preserve in case the user has changed -- them before loading the project. Free (Self.Data.View); end Unload; --------------------- -- Get_Environment -- --------------------- function Get_Environment (Self : Project_Type) return Project_Environment_Access is begin if Self = No_Project or Self.Data.Tree = null then return null; else return Self.Data.Tree.Env; end if; end Get_Environment; -------------------------- -- Is_Aggregate_Library -- -------------------------- function Is_Aggregate_Library (Self : Project_Type) return Boolean is begin return Project_Qualifier_Of (Self.Data.Node, Self.Data.Tree.Tree) = GPR.Aggregate_Library; end Is_Aggregate_Library; -------------------------- -- Is_Aggregate_Project -- -------------------------- function Is_Aggregate_Project (Self : Project_Type) return Boolean is begin return Project_Qualifier_Of (Self.Data.Node, Self.Data.Tree.Tree) in GPR.Aggregate_Project; end Is_Aggregate_Project; ------------------------- -- Is_Abstract_Project -- ------------------------- function Is_Abstract_Project (Self : Project_Type) return Boolean is begin return Project_Qualifier_Of (Self.Data.Node, Self.Data.Tree.Tree) = Abstract_Project; end Is_Abstract_Project; ----------------- -- Is_Editable -- ----------------- function Is_Editable (Project : Project_Type) return Boolean is Att : constant Attribute_Pkg_String := Build ("IDE", "Read_Only"); begin return (Project.Project_Path.Is_Writable or else not Project.Project_Path.Is_Regular_File) and then not Project.Data.Uses_Variables and then not Project.Data.Tree.Root.Is_Aggregate_Project and then Project.Data.View_Is_Complete and then (not Project.Has_Attribute (Att) or else To_Lower (Project.Attribute_Value (Att)) /= "true"); end Is_Editable; -------------- -- Finalize -- -------------- procedure Finalize is begin null; -- GPR.Finalize; -- Atree.Atree_Private_Part.Nodes.Free; end Finalize; --------- -- Put -- --------- procedure Put (Self : in out Pretty_Printer; C : Character) is pragma Unreferenced (Self); begin Ada.Text_IO.Put (C); end Put; --------- -- Put -- --------- procedure Put (Self : in out Pretty_Printer; S : String) is begin for C in S'Range loop Put (Pretty_Printer'Class (Self), S (C)); end loop; end Put; -------------- -- New_Line -- -------------- procedure New_Line (Self : in out Pretty_Printer) is begin Put (Pretty_Printer'Class (Self), ASCII.LF); end New_Line; --------- -- Put -- --------- procedure Put (Self : in out Pretty_Printer; Project : Project_Type'Class; Increment : Positive := 3; Eliminate_Empty_Case_Constructions : Boolean := False) is begin Put (Self => Self, Project => Project.Data.Node, In_Tree => Project.Data.Tree.Tree, Id => Project.Data.View, Increment => Increment, Eliminate_Empty_Case_Constructions => Eliminate_Empty_Case_Constructions); end Put; --------- -- Put -- --------- procedure Put (Self : in out Pretty_Printer'Class; Project : Project_Node_Id; In_Tree : GPR.Project_Node_Tree_Ref; Id : Project_Id := GPR.No_Project; Increment : Positive := 3; Eliminate_Empty_Case_Constructions : Boolean := False) is procedure W_Char (C : Character); procedure W_Eol; procedure W_Str (S : String); ------------ -- W_Char -- ------------ procedure W_Char (C : Character) is begin Put (Self, C); end W_Char; ----------- -- W_Eol -- ----------- procedure W_Eol is begin New_Line (Self); end W_Eol; ----------- -- W_Str -- ----------- procedure W_Str (S : String) is begin Put (Self, S); end W_Str; begin GPR.PP.Pretty_Print (Project => Project, In_Tree => In_Tree, Increment => Increment, Eliminate_Empty_Case_Constructions => Eliminate_Empty_Case_Constructions, Minimize_Empty_Lines => False, W_Char => W_Char'Unrestricted_Access, W_Eol => W_Eol'Unrestricted_Access, W_Str => W_Str'Unrestricted_Access, Backward_Compatibility => False, Id => Id); end Put; ---------- -- Node -- ---------- function Node (Project : Project_Type'Class) return GPR.Project_Node_Id is begin return Project.Data.Node; end Node; ---------- -- Tree -- ---------- function Tree (Data : Project_Tree_Data_Access) return GPR.Project_Node_Tree_Ref is begin return Data.Tree; end Tree; ------------------- -- Set_Attribute -- ------------------- procedure Set_Attribute (Self : Project_Type; Attribute : Attribute_Pkg_List; Values : GNAT.Strings.String_List; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""; Prepend : Boolean := False) is begin if not Self.Is_Editable then raise Project_Not_Editable; end if; GNATCOLL.Projects.Normalize.Set_Attribute (Self.Data.Tree, Self, Attribute, Values, Scenario, Index, Prepend); end Set_Attribute; procedure Set_Attribute (Self : Project_Type; Attribute : Attribute_Pkg_String; Value : String; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""; At_Index : Natural := 0) is begin if not Self.Is_Editable then raise Project_Not_Editable; end if; GNATCOLL.Projects.Normalize.Set_Attribute (Self.Data.Tree, Self, Attribute, Value, Scenario, Index, At_Index); end Set_Attribute; ---------------------- -- Delete_Attribute -- ---------------------- procedure Delete_Attribute (Self : Project_Type; Attribute : Attribute_Pkg_String; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := "") is begin if not Self.Is_Editable then raise Project_Not_Editable; end if; GNATCOLL.Projects.Normalize.Delete_Attribute (Self.Data.Tree, Self, String (Attribute), Scenario, Index); end Delete_Attribute; procedure Delete_Attribute (Self : Project_Type; Attribute : Attribute_Pkg_List; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := "") is begin if not Self.Is_Editable then raise Project_Not_Editable; end if; GNATCOLL.Projects.Normalize.Delete_Attribute (Self.Data.Tree, Self, String (Attribute), Scenario, Index); end Delete_Attribute; --------------------- -- Rename_And_Move -- --------------------- procedure Rename_And_Move (Self : Project_Type; New_Name : String; Directory : GNATCOLL.VFS.Virtual_File; Errors : Error_Report := null) is Old_Path : constant Virtual_File := Self.Project_Path; begin if not Self.Is_Editable then raise Project_Not_Editable; end if; GNATCOLL.Projects.Normalize.Rename_And_Move (Self.Data.Tree, Self, New_Name, Directory, Errors); Self.Data.Tree.Projects.Delete (Old_Path); Self.Data.Tree.Projects.Include (Self.Project_Path, Self); if Self.Data.View /= GPR.No_Project then Self.Data.View.Display_Name := Get_String (New_Name); end if; -- This is no longer the default project, since it was -- renamed. Otherwise, Project_Path would still return "" when saving -- the default project. Trace (Me, "Set project status to From_File"); Self.Data.Tree.Status := From_File; Reset_All_Caches (Self.Data.Tree); end Rename_And_Move; ---------------------------- -- Register_New_Attribute -- ---------------------------- function Register_New_Attribute (Name : String; Pkg : String; Is_List : Boolean := False; Indexed : Boolean := False; Case_Sensitive_Index : Boolean := False) return String is Lower_Pkg : constant String := To_Lower (Pkg); Pkg_Id : Package_Node_Id := Empty_Package; Attr_Id : Attribute_Node_Id; Attr_Kind : Defined_Attribute_Kind; Var_Kind : Defined_Variable_Kind; begin -- Need to make sure the predefined packages are already declared, or -- the new one will be discarded. GPR.Attr.Initialize; if Lower_Pkg /= "" then Pkg_Id := Package_Node_Id_Of (Get_String (Lower_Pkg)); if Pkg_Id = Empty_Package or else Pkg_Id = Unknown_Package then Trace (Me, "Register_New_Package (" & Lower_Pkg & ")"); Register_New_Package (Name => Lower_Pkg, Id => Pkg_Id); if Pkg_Id = Empty_Package or else Pkg_Id = Unknown_Package then Trace (Me, "Error registering new package"); end if; end if; end if; if Pkg_Id = Empty_Package then Attr_Id := Attribute_Node_Id_Of (Name => Get_String (Name), Starting_At => GPR.Attr.Attribute_First); else Attr_Id := Attribute_Node_Id_Of (Name => Get_String (Name), Starting_At => First_Attribute_Of (Pkg_Id)); end if; if Is_List then Var_Kind := GPR.List; else Var_Kind := GPR.Single; end if; if Indexed then if Case_Sensitive_Index then Attr_Kind := GPR.Attr.Associative_Array; else Attr_Kind := GPR.Attr.Case_Insensitive_Associative_Array; end if; -- Priority is given to the registered type if Attr_Id /= Empty_Attribute then Attr_Kind := Attribute_Kind_Of (Attr_Id); if Attr_Kind = Attribute_Kind'(Single) then Attr_Kind := GPR.Attr.Associative_Array; end if; end if; else Attr_Kind := Attribute_Kind'(Single); end if; if Attr_Id = Empty_Attribute then if Lower_Pkg = "" then return "Project attributes cannot be added at the top level of" & " project files, only in packages"; else if Active (Me) then Trace (Me, "Register_New_Attribute (" & Name & ", " & Lower_Pkg & ", " & Attr_Kind'Img & ", " & Var_Kind'Img & ")"); end if; Register_New_Attribute (Name => Name, In_Package => Pkg_Id, Attr_Kind => Attr_Kind, Var_Kind => Var_Kind, Index_Is_File_Name => False, Opt_Index => False); end if; else if Attribute_Kind_Of (Attr_Id) /= Attr_Kind or else Variable_Kind_Of (Attr_Id) /= Var_Kind then return Name & ": attributes was already defined but with a" & " different type"; end if; end if; return ""; end Register_New_Attribute; ---------------------------------- -- Register_Specific_Attributes -- ---------------------------------- procedure Register_Specific_Attributes is begin if Specific_Attributes_Registered then -- Already registered during previous loads, nothing to do. return; end if; if not Attribute_Registered ("Artifacts_Dir", "IDE") then declare S : constant String := Register_New_Attribute ("Artifacts_Dir", "IDE"); begin if S /= "" then Trace (Me, "Cannot register attribute IDE'Artefact_Dir: " & S); end if; end; end if; if not Attribute_Registered ("Read_Only", "IDE") then declare S : constant String := Register_New_Attribute ("Read_Only", "IDE"); begin if S /= "" then Trace (Me, "Cannot register attribute IDE'Artefact_Dir: " & S); end if; end; end if; -- If it didn't work the first time it won't work at all, no use trying -- again. Specific_Attributes_Registered := True; end Register_Specific_Attributes; ---------- -- Save -- ---------- function Save (Project : Project_Type; Force : Boolean := False; Errors : Error_Report := null) return Boolean is File : Ada.Text_IO.File_Type; type File_Pretty_Printer is new Pretty_Printer with null record; overriding procedure Put (Self : in out File_Pretty_Printer; C : Character); overriding procedure Put (Self : in out File_Pretty_Printer; S : String); --------- -- Put -- --------- overriding procedure Put (Self : in out File_Pretty_Printer; C : Character) is pragma Unreferenced (Self); begin Put (File, C); end Put; overriding procedure Put (Self : in out File_Pretty_Printer; S : String) is pragma Unreferenced (Self); begin Put (File, S); end Put; PP : File_Pretty_Printer; begin if not Project.Is_Editable then raise Project_Not_Editable; end if; if not Is_Regular_File (Project.Project_Path) or else Project.Data.Modified or else Force then if Is_Regular_File (Project_Path (Project)) and then not Is_Writable (Project_Path (Project)) then if Errors /= null then Errors ("The file " & Display_Full_Name (Project_Path (Project)) & " is not writable. Project not saved"); end if; Trace (Me, "Project file not writable: " & Project_Path (Project).Display_Full_Name); return False; end if; declare Filename : constant Virtual_File := Project_Path (Project); Dirname : Virtual_File renames Dir (Filename); begin Trace (Me, "Save_Project: Creating new file " & Filename.Display_Full_Name); begin Ada.Directories.Create_Path (Dirname.Display_Full_Name); exception when Ada.Directories.Name_Error | Ada.Directories.Use_Error => Trace (Me, "Couldn't create directory " & Dirname.Display_Full_Name); if Errors /= null then Errors ("Couldn't create directory " & Dirname.Display_Full_Name); end if; return False; end; Normalize_Cases (Project.Data.Tree.Tree, Project); Create (File, Mode => Out_File, Name => +Full_Name (Filename)); PP.Put (Project => Project); Close (File); Project.Data.Modified := False; Trace (Me, "Set project status to From_File"); Project.Data.Tree.Status := From_File; return True; exception when Ada.Text_IO.Name_Error => Trace (Me, "Couldn't create " & Filename.Display_Full_Name); if Errors /= null then Errors ("Couldn't create file " & Filename.Display_Full_Name); end if; return False; end; end if; return False; end Save; -------------- -- Modified -- -------------- function Modified (Project : Project_Type; Recursive : Boolean := False) return Boolean is Iter : Inner_Project_Iterator := Start (Project, Recursive); P : Project_Type; begin loop P := Current (Iter); exit when P = GNATCOLL.Projects.No_Project; if P.Data.Modified then return True; end if; Next (Iter); end loop; return False; end Modified; ------------------ -- Set_Modified -- ------------------ procedure Set_Modified (Project : Project_Type; Modified : Boolean) is begin Project.Data.Modified := Modified; end Set_Modified; ----------------------------- -- Remove_Imported_Project -- ----------------------------- procedure Remove_Imported_Project (Project : Project_Type; Imported_Project : Project_Type) is Tree : constant GPR.Project_Node_Tree_Ref := Project.Data.Tree.Tree; With_Clause : Project_Node_Id := First_With_Clause_Of (Project.Node, Tree); Next : Project_Node_Id; Iter : Project_Iterator; Remove : Boolean := True; Basename : constant Filesystem_String := Base_Name (Imported_Project.Project_Path.Full_Name, Project_File_Extension); Dep_ID : constant Name_Id := Get_String (+Basename); Tree_Node : constant GPR.Project_Node_Tree_Ref := Project.Data.Tree.Tree; begin if not Project.Is_Editable then raise Project_Not_Editable; end if; if With_Clause /= Empty_Project_Node and then GPR.Tree.Name_Of (With_Clause, Tree) = GPR.Tree.Name_Of (Imported_Project.Node, Tree) then Set_First_With_Clause_Of (Project.Node, Tree, Next_With_Clause_Of (With_Clause, Tree)); else loop Next := Next_With_Clause_Of (With_Clause, Tree); exit when Next = Empty_Project_Node; if GPR.Tree.Name_Of (Next, Tree) = GPR.Tree.Name_Of (Imported_Project.Node, Tree) then Set_Next_With_Clause_Of (With_Clause, Tree, Next_With_Clause_Of (Next, Tree)); end if; With_Clause := Next; end loop; end if; Project.Data.Modified := True; -- Need to reset all the caches, since the caches contain the indirect -- dependencies as well. Reset_All_Caches (Project.Data.Tree); Iter := Start (Project.Data.Tree.Root, Recursive => True); while Current (Iter) /= No_Project loop Trace (Me, " " & Current (Iter).Project_Path.Display_Full_Name); if Current (Iter) = Imported_Project then Remove := False; exit; end if; Projects.Next (Iter); end loop; if Remove and then Dep_ID /= No_Name then Tree_Private_Part.Projects_Htable.Remove (Tree_Node.Projects_HT, Dep_ID); end if; end Remove_Imported_Project; ---------------------- -- Reset_All_Caches -- ---------------------- procedure Reset_All_Caches (Tree : Project_Tree_Data_Access) is Cursor : Project_Htables.Cursor := Tree.Projects.First; begin while Has_Element (Cursor) loop Unchecked_Free (Element (Cursor).Data.Imported_Projects.Items); Unchecked_Free (Element (Cursor).Data.Importing_Projects); Next (Cursor); end loop; end Reset_All_Caches; -------------------------- -- Add_Imported_Project -- -------------------------- function Add_Imported_Project (Tree : Project_Tree; Project : Project_Type'Class; Imported_Project_Location : GNATCOLL.VFS.Virtual_File; Packages_To_Check : GNAT.Strings.String_List_Access := No_Packs; Errors : Error_Report := null; Use_Relative_Path : Boolean := True; Use_Base_Name : Boolean := False; Limited_With : Boolean := False) return Import_Project_Error is Tree_Node : constant GPR.Project_Node_Tree_Ref := Project.Data.Tree.Tree; use GPR.Tree_Private_Part; procedure Fail (S : String); ---------- -- Fail -- ---------- procedure Fail (S : String) is begin if Errors /= null then Errors (S); end if; end Fail; Basename : constant Filesystem_String := Base_Name (Imported_Project_Location, Project_File_Extension); Imported_Project : Project_Node_Id := Empty_Project_Node; Dep_ID : Name_Id; Dep_Name : GPR.Tree_Private_Part.Project_Name_And_Node; Error : Import_Project_Error; begin if not Project.Is_Editable then raise Project_Not_Editable; end if; GPR.Output.Set_Special_Output (Fail'Unrestricted_Access); GPR.Com.Fail := Fail'Unrestricted_Access; Dep_ID := Get_String (+Basename); Dep_Name := Tree_Private_Part.Projects_Htable.Get (Tree_Node.Projects_HT, Dep_ID); if Dep_Name /= No_Project_Name_And_Node then -- ??? We used to compare on the build server, but that might not be -- necessary (and we do not have access to this information in -- GNATCOLL in any case). if not File_Equal (Format_Pathname (+Get_String (Path_Name_Of (Dep_Name.Node, Tree_Node))), Imported_Project_Location.Full_Name, Local_Host) then Fail ("A different project with the same name" & " already exists in the project tree."); GPR.Output.Cancel_Special_Output; GPR.Com.Fail := null; return Project_Already_Exists; else Imported_Project := Dep_Name.Node; end if; else Override_Flags (Tree.Data.Env.Env, Create_Flags (null, False)); GPR.Part.Parse (Tree_Node, Imported_Project, +Full_Name (Imported_Project_Location), Packages_To_Check => Packages_To_Check, Is_Config_File => False, Current_Directory => Get_Current_Dir, Env => Tree.Data.Env.Env); GPR.Err.Finalize; end if; if Imported_Project = Empty_Project_Node then Trace (Me, "Add_Imported_Project: imported project not found (" & Imported_Project_Location.Display_Full_Name & ")"); GPR.Output.Cancel_Special_Output; GPR.Com.Fail := null; return Imported_Project_Not_Found; end if; Compute_Importing_Projects (Project, Project.Data.Tree.Root); Error := Add_Imported_Project (Tree => Project.Data.Tree, Project => Project, Imported_Project => Tree.Instance_From_Node (Tree, Imported_Project), Errors => Errors, Use_Relative_Path => Use_Relative_Path, Use_Base_Name => Use_Base_Name, Limited_With => Limited_With); if Error = Success then Create_Project_Instances (Tree, Tree, With_View => False); end if; return Error; end Add_Imported_Project; -------------------------- -- Add_Imported_Project -- -------------------------- function Add_Imported_Project (Project : Project_Type; Imported_Project : Project_Type; Errors : Error_Report := null; Use_Relative_Path : Boolean := True; Use_Base_Name : Boolean := False; Limited_With : Boolean := False) return Import_Project_Error is begin if not Project.Is_Editable then raise Project_Not_Editable; end if; Compute_Importing_Projects (Project, Project.Data.Tree.Root); return GNATCOLL.Projects.Normalize.Add_Imported_Project (Tree => Project.Data.Tree, Project => Project, Imported_Project => Imported_Project, Errors => Errors, Use_Relative_Path => Use_Relative_Path, Use_Base_Name => Use_Base_Name, Limited_With => Limited_With); -- No need for Create_Project_Instances in this version, since the -- imported_project was already in memory. end Add_Imported_Project; ------------------------------ -- Delete_Scenario_Variable -- ------------------------------ procedure Delete_Scenario_Variable (Tree : Project_Tree'Class; External_Name : String; Keep_Choice : String; Delete_Direct_References : Boolean := True) is begin if not Tree.Root_Project.Is_Editable then Trace (Me, "Project is not editable"); return; end if; GNATCOLL.Projects.Normalize.Delete_Scenario_Variable (Tree.Data, Tree.Root_Project, External_Name, Keep_Choice, Delete_Direct_References); -- Mark all projects in the hierarchy as modified, since they are -- potentially all impacted. declare Cursor : Project_Htables.Cursor := Tree.Data.Projects.First; begin while Has_Element (Cursor) loop Element (Cursor).Set_Modified (True); Next (Cursor); end loop; end; end Delete_Scenario_Variable; ----------------- -- Rename_Path -- ----------------- function Rename_Path (Self : Project_Type; Old_Path : GNATCOLL.VFS.Virtual_File; New_Path : GNATCOLL.VFS.Virtual_File; Use_Relative_Paths : Boolean) return Boolean is begin if not Self.Is_Editable then raise Project_Not_Editable; end if; return GNATCOLL.Projects.Normalize.Rename_Path (Self.Data.Tree, Self, Old_Path, New_Path, Use_Relative_Paths); end Rename_Path; -------------------- -- Create_Project -- -------------------- function Create_Project (Tree : Project_Tree'Class; Name : String; Path : GNATCOLL.VFS.Virtual_File) return Project_Type is D : constant Filesystem_String := Name_As_Directory (Path.Full_Name) & (+Translate (To_Lower (Name), To_Mapping (".", "-"))) & GNATCOLL.Projects.Project_File_Extension; Project : constant Project_Node_Id := GPR.Tree.Create_Project (In_Tree => Tree.Data.Tree, Name => Get_String (Name), Full_Path => Path_Name_Type (Get_String (+D)), Is_Config_File => False); P : Project_Type; begin P := Tree.Instance_From_Node (Tree, Project); P.Set_Modified (True); return P; end Create_Project; -------------------------- -- Set_Extended_Project -- -------------------------- procedure Set_Extended_Project (Self : GNATCOLL.Projects.Project_Type; Extended : GNATCOLL.Projects.Project_Type; Extend_All : Boolean := False; Use_Relative_Paths : Boolean := False) is begin if not Self.Is_Editable then raise Project_Not_Editable; end if; if Use_Relative_Paths then declare Path : constant Filesystem_String := Relative_Path (Extended.Project_Path, Self.Project_Path); begin Set_Extended_Project_Path_Of (Self.Data.Node, Self.Data.Tree.Tree, To => Path_Name_Type (Get_String (+Path))); end; else Set_Extended_Project_Path_Of (Self.Data.Node, Self.Data.Tree.Tree, To => Path_Name_Type (Get_String (+Extended.Project_Path.Full_Name))); end if; Set_Extended_Project_Of (Project_Declaration_Of (Self.Data.Node, Self.Data.Tree.Tree), Self.Data.Tree.Tree, To => Extended.Node); if Extend_All then Set_Is_Extending_All (Self.Data.Node, Self.Data.Tree.Tree); end if; end Set_Extended_Project; ------------------------------ -- Create_Scenario_Variable -- ------------------------------ function Create_Scenario_Variable (Project : Project_Type; Name : String; Type_Name : String; External_Name : String) return Scenario_Variable is Tree_Node : constant GPR.Project_Node_Tree_Ref := Project.Data.Tree.Tree; Typ, Var : Project_Node_Id; begin if not Project.Is_Editable then Trace (Me, "Project is not editable"); return GNATCOLL.Projects.No_Variable; end if; GNATCOLL.Projects.Normalize.Normalize (Project.Data.Tree, Project); Typ := Create_Type (Tree_Node, Project.Data.Node, Type_Name); Var := Create_Typed_Variable (Tree_Node, Project.Data.Node, Name, Typ, Add_Before_First_Case_Or_Pkg => True); Set_Value_As_External (Tree_Node, Var, External_Name); Project.Set_Modified (True); -- Clear the cache Unchecked_Free (Project.Data.Tree.Env.Scenario_Variables); return (Ext_Name => Get_String (External_Name), Var_Name => No_Name, Default => No_Name, Value => No_Name, String_Type => Typ, Tree_Ref => Tree_Node, First_Project_Path => GPR.No_Path); end Create_Scenario_Variable; -------------------------- -- Change_External_Name -- -------------------------- procedure Change_External_Name (Tree : Project_Tree'Class; Variable : in out Scenario_Variable; New_Name : String) is Tree_Node : constant GPR.Project_Node_Tree_Ref := Tree.Data.Tree; procedure Callback (Project, Parent, Node, Choice : Project_Node_Id); -- Called for each matching node for the environment variable -------------- -- Callback -- -------------- procedure Callback (Project, Parent, Node, Choice : Project_Node_Id) is pragma Unreferenced (Project, Parent, Choice); begin if Kind_Of (Node, Tree_Node) = N_External_Value then Set_String_Value_Of (External_Reference_Of (Node, Tree_Node), Tree_Node, Get_String (New_Name)); end if; end Callback; Ext_Ref : constant Name_Id := Get_String (External_Name (Variable)); begin if not Tree.Root_Project.Is_Editable then Trace (Me, "Project is not editable"); return; end if; GNATCOLL.Projects.Normalize.Normalize (Tree.Data, Tree.Root_Project); For_Each_Environment_Variable (Tree.Data.Tree, Tree.Root_Project, Ext_Ref, No_Name, Callback'Unrestricted_Access); Tree.Root_Project.Set_Modified (True); -- Create the new variable, to avoid errors when computing the view of -- the project. Variable.Ext_Name := Get_String (New_Name); Tree.Change_Environment ((1 => Variable)); end Change_External_Name; ----------------------- -- Set_Default_Value -- ----------------------- procedure Set_Default_Value (Tree : Project_Tree'Class; External_Name : String; Default : String) is Tree_Node : constant GPR.Project_Node_Tree_Ref := Tree.Data.Tree; procedure Callback (Project, Parent, Node, Choice : Project_Node_Id); -- Called for each matching node for the environment variable -------------- -- Callback -- -------------- procedure Callback (Project, Parent, Node, Choice : Project_Node_Id) is pragma Unreferenced (Project, Parent, Choice); begin if Kind_Of (Node, Tree_Node) = N_Typed_Variable_Declaration then Set_External_Default_Of (Current_Term (First_Term (Expression_Of (Node, Tree_Node), Tree_Node), Tree_Node), Tree_Node, Enclose_In_Expression (Create_Literal_String (Get_String (Default), Tree_Node), Tree_Node)); end if; end Callback; begin if not Tree.Root_Project.Is_Editable then Trace (Me, "Project is not editable"); return; end if; For_Each_Environment_Variable (Tree.Data.Tree, Tree.Root_Project, Get_String (External_Name), No_Name, Callback'Unrestricted_Access); Tree.Root_Project.Set_Modified (True); end Set_Default_Value; ------------------ -- Rename_Value -- ------------------ procedure Rename_Value (Tree : Project_Tree'Class; External_Name : String; Old_Value : String; New_Value : String) is Tree_N : constant GPR.Project_Node_Tree_Ref := Tree.Data.Tree; Old_V : constant Name_Id := Get_String (Old_Value); New_V : constant Name_Id := Get_String (New_Value); N : constant Name_Id := Get_String (External_Name); procedure Callback (Project, Parent, Node, Choice : Project_Node_Id); -- Called for each matching node for the environment variable -------------- -- Callback -- -------------- procedure Callback (Project, Parent, Node, Choice : Project_Node_Id) is pragma Unreferenced (Project, Parent); C : Project_Node_Id; begin case Kind_Of (Node, Tree_N) is when N_External_Value => if External_Default_Of (Node, Tree_N) /= Empty_Project_Node and then Expression_As_String (Tree_N, External_Default_Of (Node, Tree_N)) = Old_V then if Kind_Of (External_Default_Of (Node, Tree_N), Tree_N) = N_Literal_String then Set_String_Value_Of (External_Default_Of (Node, Tree_N), Tree_N, New_V); else Set_External_Default_Of (Node, Tree_N, Create_Literal_String (New_V, Tree_N)); end if; end if; when N_String_Type_Declaration => C := First_Literal_String (Node, Tree_N); while C /= Empty_Project_Node loop if String_Value_Of (C, Tree_N) = Old_V then Set_String_Value_Of (C, Tree_N, New_V); exit; end if; C := Next_Literal_String (C, Tree_N); end loop; when N_Case_Item => Set_String_Value_Of (Choice, Tree_N, New_V); when others => null; end case; end Callback; begin if not Tree.Root_Project.Is_Editable then Trace (Me, "Project is not editable"); return; end if; GNATCOLL.Projects.Normalize.Normalize (Tree.Data, Tree.Root_Project); For_Each_Environment_Variable (Tree.Data.Tree, Tree.Root_Project, N, Old_V, Callback'Unrestricted_Access); if GPR.Ext.Value_Of (Tree.Data.Env.Env.External, N) /= No_Name and then GPR.Ext.Value_Of (Tree.Data.Env.Env.External, N) = Old_V then GPR.Ext.Add (Tree.Data.Env.Env.External, External_Name, New_Value, GPR.Ext.From_Command_Line); end if; Tree.Root_Project.Set_Modified (True); end Rename_Value; ------------------ -- Remove_Value -- ------------------ procedure Remove_Value (Tree : Project_Tree'Class; External_Name : String; Value : String) is Tree_N : constant GPR.Project_Node_Tree_Ref := Tree.Data.Tree; Delete_Variable : exception; Type_Decl : Project_Node_Id := Empty_Project_Node; V_Name : constant Name_Id := Get_String (Value); Ext_Var : constant Name_Id := Get_String (External_Name); procedure Callback (Project, Parent, Node, Choice : Project_Node_Id); -- Called for each matching node for the environment variable -------------- -- Callback -- -------------- procedure Callback (Project, Parent, Node, Choice : Project_Node_Id) is pragma Unreferenced (Project, Choice); C, C2 : Project_Node_Id; begin case Kind_Of (Node, Tree_N) is when N_String_Type_Declaration => Type_Decl := Node; C := First_Literal_String (Node, Tree_N); if Next_Literal_String (C, Tree_N) = Empty_Project_Node then raise Delete_Variable; end if; if String_Value_Of (C, Tree_N) = V_Name then Set_First_Literal_String (Node, Tree_N, Next_Literal_String (C, Tree_N)); return; end if; loop C2 := Next_Literal_String (C, Tree_N); exit when C2 = Empty_Project_Node; if String_Value_Of (C2, Tree_N) = V_Name then Set_Next_Literal_String (C, Tree_N, Next_Literal_String (C2, Tree_N)); exit; end if; C := C2; end loop; when N_External_Value => if External_Default_Of (Node, Tree_N) /= Empty_Project_Node and then String_Value_Of (External_Default_Of (Node, Tree_N), Tree_N) = V_Name then Set_External_Default_Of (Node, Tree_N, Empty_Project_Node); end if; when N_Case_Item => C := First_Case_Item_Of (Current_Item_Node (Parent, Tree_N), Tree_N); if C = Node then Set_First_Case_Item_Of (Current_Item_Node (Parent, Tree_N), Tree_N, Next_Case_Item (C, Tree_N)); return; end if; loop C2 := Next_Case_Item (C, Tree_N); exit when C2 = Empty_Project_Node; if C2 = Node then Set_Next_Case_Item (C, Tree_N, Next_Case_Item (C2, Tree_N)); end if; C := C2; end loop; when others => null; end case; end Callback; begin if not Tree.Root_Project.Is_Editable then Trace (Me, "Project is not editable"); return; end if; GNATCOLL.Projects.Normalize.Normalize (Tree.Data, Tree.Root_Project); For_Each_Environment_Variable (Tree.Data.Tree, Tree.Root_Project, Ext_Var, Get_String (Value), Callback'Unrestricted_Access); -- Reset the value of the external variable if needed if GPR.Ext.Value_Of (Tree.Data.Env.Env.External, Ext_Var) = V_Name then if Type_Decl /= Empty_Project_Node then GPR.Ext.Add (Tree.Data.Env.Env.External, External_Name, Get_String (String_Value_Of (First_Literal_String (Type_Decl, Tree_N), Tree_N)), GPR.Ext.From_Command_Line); else GPR.Ext.Add (Tree.Data.Env.Env.External, External_Name, "", GPR.Ext.From_Command_Line); end if; end if; Tree.Root_Project.Set_Modified (True); exception when Delete_Variable => Tree.Delete_Scenario_Variable (External_Name => External_Name, Keep_Choice => Value, Delete_Direct_References => False); end Remove_Value; ---------------- -- Add_Values -- ---------------- procedure Add_Values (Tree : Project_Tree'Class; Variable : Scenario_Variable; Values : GNAT.Strings.String_List) is Tree_N : constant GPR.Project_Node_Tree_Ref := Tree.Data.Tree; Type_Node, Var : Project_Node_Id; Iter : Inner_Project_Iterator := Tree.Root_Project.Start; P : Project_Type; begin loop P := Current (Iter); exit when P = No_Project; if not P.Is_Editable then Trace (Me, "Project is not editable: " & P.Name); return; end if; GNATCOLL.Projects.Normalize.Normalize (Tree.Data, P); Var := Find_Scenario_Variable (Tree_N, P, External_Name (Variable)); -- If variable is defined in the current project, then modify the -- type to Values. if Var /= Empty_Project_Node then Type_Node := String_Type_Of (Var, Tree_N); pragma Assert (Type_Node /= Empty_Project_Node); -- Set_First_Literal_String (Type_Node, Empty_Node); for J in Values'Range loop Add_Possible_Value (Tree_N, Type_Node, Values (J).all); end loop; P.Set_Modified (True); end if; Next (Iter); end loop; end Add_Values; ---------- -- Free -- ---------- procedure Free (Self : in out Project_Environment_Access) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Project_Environment'Class, Project_Environment_Access); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Naming_Scheme_Record, Naming_Scheme_Access); NS : Naming_Scheme_Access; begin if Self /= null then while Self.Naming_Schemes /= null loop NS := Self.Naming_Schemes; Self.Naming_Schemes := NS.Next; Free (NS.Language); Free (NS.Default_Spec_Suffix); Free (NS.Default_Body_Suffix); Free (NS.Obj_Suffix); Unchecked_Free (NS); end loop; Unchecked_Free (Self.Predefined_Object_Path); Unchecked_Free (Self.Predefined_Source_Path); Unchecked_Free (Self.Predefined_Project_Path); Unchecked_Free (Self.Predefined_Source_Files); Free (Self.Xrefs_Subdir); Self.Extensions.Clear; Free (Self.Save_Config_File); Free (Self.Default_Gnatls); Free (Self.Gnatls); Free (Self.Forced_Target); Free (Self.Forced_Runtime); if not (Self.Packages_To_Check in All_Packs | No_Packs) then Free (Self.Packages_To_Check); end if; Free (Self.Env); Unchecked_Free (Self); end if; end Free; ---------- -- Free -- ---------- procedure Free (Self : in out Project_Tree_Data_Access) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Project_Tree_Data, Project_Tree_Data_Access); begin if Self /= null then if Self.Tree /= null then GPR.Tree_Private_Part.Project_Node_Table.Free (Self.Tree.Project_Nodes); Free (Self.Tree); end if; if not Self.Is_Aggregated then Self.Projects.Clear; end if; Unchecked_Free (Self); end if; end Free; ---------- -- Free -- ---------- procedure Free (Self : in out Project_Tree_Access) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Project_Tree'Class, Project_Tree_Access); begin if Self /= null then Free (Self.Data); Unchecked_Free (Self); end if; end Free; ------------ -- Status -- ------------ function Status (Self : Project_Tree) return Project_Status is begin if Self.Data = null then return Empty; else return Self.Data.Status; end if; end Status; ---------------- -- Set_Status -- ---------------- procedure Set_Status (Self : Project_Tree; Status : Project_Status) is begin Trace (Me, "set project status to " & Status'Img); Self.Data.Status := Status; end Set_Status; ------------ -- Append -- ------------ procedure Append (Self : in out Path_Name_Array; Path : GPR.Path_Name_Type) is Tmp : Path_Name_Id_Array_Access; begin if Self.Items = null then Self.Items := new Path_Name_Id_Array (1 .. 4); Self.Last := 0; elsif Self.Last = Self.Items'Last then Tmp := Self.Items; Self.Items := new Path_Name_Id_Array (1 .. Self.Items'Last * 2); Self.Items (Tmp'Range) := Tmp.all; Unchecked_Free (Tmp); end if; Self.Last := Self.Last + 1; Self.Items (Self.Last) := Path; end Append; -------------------------- -- Set_Save_Config_File -- -------------------------- procedure Set_Save_Config_File (Self : in out Project_Environment; Name : GNATCOLL.VFS.Filesystem_String) is begin Self.Save_Config_File := new String'(+Name); end Set_Save_Config_File; ----------------------------------------------- -- Set_Disable_Use_Of_TTY_Process_Descriptor -- ----------------------------------------------- procedure Set_Disable_Use_Of_TTY_Process_Descriptor (Self : in out Project_Environment; Disabled : Boolean) is begin Self.TTY_Process_Descriptor_Disabled := Disabled; end Set_Disable_Use_Of_TTY_Process_Descriptor; --------------------------- -- Set_Host_Targets_List -- --------------------------- procedure Set_Host_Targets_List is Gprbuild_Path : Filesystem_String_Access; KB_Dir, TS_File : GNATCOLL.VFS.Virtual_File; KB : GPR.Knowledge.Knowledge_Base; use GPR.Knowledge; use GPR.Knowledge.String_Lists; TS_Id : GPR.Knowledge.Targets_Set_Id; use DOM.Core, DOM.Core.Nodes; use Input_Sources.File; use Sax.Readers; use Schema.Dom_Readers; Input : File_Input; Reader : Schema.Dom_Readers.Tree_Reader; File_Node : DOM.Core.Node; N, N2 : DOM.Core.Node; begin Trace (Me, "Set_Host_Targets_List"); if Host_Targets_List_Set then -- No point reparsing KB more than once. return; end if; Host_Targets_List_Set := True; Gprbuild_Path := Locate_Exec_On_Path ("gprbuild"); if Gprbuild_Path = null then Trace (Me, "Gprbuild not found on path"); return; end if; KB_Dir := Get_Parent (Create (Dir_Name (Gprbuild_Path.all))); KB_Dir := Join (Join (KB_Dir, "share"), "gprconfig"); Free (Gprbuild_Path); GPR.Knowledge.Parse_Knowledge_Base (KB, KB_Dir.Display_Full_Name, Parse_Compiler_Info => False); GPR.Knowledge.Get_Targets_Set (KB, GPR.Sdefault.Hostname, TS_Id); Host_Targets_List := GPR.Knowledge.Get_Fallback_List (Base => KB, On_Target => TS_Id); Host_Targets_List.Append (GPR.Knowledge.Normalized_Target (KB, TS_Id)); GPR.Knowledge.Free_Knowledge_Base (KB); TS_File := Join (KB_Dir, "targetset.xml"); if not TS_File.Is_Regular_File then Trace (Me, "targetset.xml not found"); return; end if; Open (TS_File.Display_Full_Name, Input); Reader.Set_Feature (Schema_Validation_Feature, False); Reader.Set_Feature (Validation_Feature, False); -- Do not use DTD Parse (Reader, Input); Close (Input); File_Node := DOM.Core.Documents.Get_Element (Get_Tree (Reader)); if Node_Name (File_Node) = "gprconfig" then N := First_Child (File_Node); while N /= null loop if Node_Name (N) = "targetset" then declare Attr : constant DOM.Core.Node := Get_Named_Item (Attributes (N), "canonical"); TS_Info : Targetset_Info; use Ada.Strings.Unbounded; begin if Attr /= null then TS_Info.Canonical_Name := To_Unbounded_String (Node_Value (Attr)); end if; N2 := First_Child (N); while N2 /= null loop if Node_Name (N2) = "target" then if TS_Info.Canonical_Name = Null_Unbounded_String then TS_Info.Canonical_Name := To_Unbounded_String (Node_Value (First_Child (N2))); end if; TS_Info.Regexp_Imgs.Append (Node_Value (First_Child (N2))); end if; N2 := Next_Sibling (N2); end loop; Normalization_Dictionary.Include (TS_Info); end; end if; N := Next_Sibling (N); end loop; end if; declare Doc : Document := Get_Tree (Reader); begin Free (Doc); end; Free (Reader); end Set_Host_Targets_List; ------------------------- -- Aggregated_Projects -- ------------------------- function Aggregated_Projects (Project : Project_Type; Unwind_Aggregated : Boolean := True) return Project_Array_Access is P : Project_Type; Aggregated : Aggregated_Project_List; P_Files_Agg : Project_Array_Access; Result : Project_Array_Access := new Project_Array'(Empty_Project_Array); procedure Append (Files : in out Project_Array_Access; P : Project_Type); procedure Append (Files : in out Project_Array_Access; P : Project_Type) is Tmp : Project_Array_Access; begin if Files = null then Files := new Project_Array'(1 => P); else Tmp := new Project_Array (1 .. Files'Length + 1); Tmp (1 .. Files'Length) := Files.all; Tmp (Tmp'Last) := P; Unchecked_Free (Files); Files := Tmp; end if; end Append; begin if Project.Get_View = GPR.No_Project then -- View has not been computed for this project. return Result; end if; if not Project.Is_Aggregate_Project then return Result; end if; Aggregated := Project.Data.View.Aggregated_Projects; while Aggregated /= null loop P := Project_Type (Project_From_Path (Project.Data.Tree_For_Map, Aggregated.Path)); if Unwind_Aggregated and then P.Is_Aggregate_Project then P_Files_Agg := P.Aggregated_Projects; for P_File_Agg of P_Files_Agg.all loop Append (Result, P_File_Agg); end loop; Unchecked_Free (P_Files_Agg); else Append (Result, P); end if; Aggregated := Aggregated.Next; end loop; return Result; end Aggregated_Projects; --------------------------- -- Normalize_Target_Name -- --------------------------- function Normalize_Target_Name (Target_Name : String) return String is use Ada.Strings.Unbounded; begin if Target_Name = "" then return ""; end if; for TS_Info of Normalization_Dictionary loop for Regexp_Img of TS_Info.Regexp_Imgs loop declare Pattern : constant Pattern_Matcher := Compile ("^" & Regexp_Img & "$"); begin if Match (Pattern, Target_Name) > Target_Name'First - 1 then return To_String (TS_Info.Canonical_Name); end if; exception when Expression_Error => -- We do not care about possible errors, if the regexp is -- bad we simply ignore it for normalization purposes. null; end; end loop; end loop; return Target_Name; end Normalize_Target_Name; begin -- GPR.Initialize; -- Csets.Initialize; Snames.Initialize; -- Disable verbose messages from project manager, not useful in GPS Opt.Quiet_Output := True; -- Unchecked_Shared_Lib_Imports is only relevant for builders Opt.Unchecked_Shared_Lib_Imports := True; end GNATCOLL.Projects; gnatcoll-core-21.0.0/src/gnatcoll-config.adb0000644000175000017500000003762613661715457020527 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Strings.Maps; use Ada.Strings.Maps; with GNAT.Directory_Operations; use GNAT.Directory_Operations; with GNAT.OS_Lib; use GNAT.OS_Lib; with GNATCOLL.Mmap; use GNATCOLL.Mmap; with GNATCOLL.Strings; use GNATCOLL.Strings; with GNATCOLL.Templates; use GNATCOLL.Templates; with GNATCOLL.Utils; use GNATCOLL.Utils; with GNATCOLL.VFS; use GNATCOLL.VFS; package body GNATCOLL.Config is use String_Maps; No_Value : constant Config_Value := (Len => 0, System_Id => Null_XString, Value => (others => ' ')); Whitespaces : constant Character_Set := To_Set (" " & ASCII.CR & ASCII.HT & ASCII.LF & ASCII.VT & ASCII.FF); function Internal_Get (Self : Config_Pool; Key : String; Section : String := Section_From_Key) return Config_Value; -- Internal version of Get function At_Index (Value : Config_Value; Index : Natural := Whole_Value) return String; -- Extract an element from a comma-separated list function Substitute (Self : INI_Parser'Class; Value : String) return String; -- Substitute various strings in the value read from the config file, -- for instance $HOME. ------------------- -- Set_System_Id -- ------------------- procedure Set_System_Id (Self : in out Config_Parser; System_ID : String) is begin Self.System_ID := To_XString (Normalize_Pathname (System_ID)); end Set_System_Id; ---------------- -- As_Integer -- ---------------- function As_Integer (Self : Config_Parser) return Integer is begin return Integer'Value (Value (Config_Parser'Class (Self))); end As_Integer; ---------------- -- As_Boolean -- ---------------- function As_Boolean (Self : Config_Parser) return Boolean is begin return Boolean'Value (Value (Config_Parser'Class (Self))); end As_Boolean; ---------------------- -- As_Absolute_File -- ---------------------- function As_Absolute_File (Self : Config_Parser) return String is Val : constant String := Value (Config_Parser'Class (Self)); begin if Val = "" then return ""; elsif Val (Val'First) = '/' then return Val; else return Normalize_Pathname (Val, To_String (Self.System_ID)); end if; end As_Absolute_File; --------------------- -- As_Absolute_Dir -- --------------------- function As_Absolute_Dir (Self : Config_Parser) return String is V : constant String := As_Absolute_File (Config_Parser'Class (Self)); begin if V = "" then return ""; elsif V (V'Last) = Directory_Separator then return V; else return V & Directory_Separator; end if; end As_Absolute_Dir; ---------- -- Open -- ---------- procedure Open (Self : in out File_Config_Parser; Filename : String) is F : Mapped_File; Str : Str_Access; begin F := Open_Read (Filename); Read (F); Str := Data (F); Self.Contents := To_XString (String (Str (1 .. Last (F)))); Self.System_ID := To_XString (Normalize_Pathname (Dir_Name (Filename))); Self.First := 1; Close (F); end Open; ------------ -- At_End -- ------------ overriding function At_End (Self : File_Config_Parser) return Boolean is begin return Self.First > Length (Self.Contents); end At_End; ---------- -- Open -- ---------- overriding procedure Open (Self : in out INI_Parser; Filename : String) is begin Open (File_Config_Parser (Self), Filename); Self.Eol := 0; Self.Current_Section := To_XString (""); Next (Self); end Open; ---------- -- Next -- ---------- overriding procedure Next (Self : in out INI_Parser) is Eol : Integer; First_Non_WS : Integer; Last_Non_WS : Integer; Last : constant Integer := Length (Self.Contents); Comment : constant Integer := Length (Self.Comment_Start); begin -- Mark begining of the line. Self.First := Self.Eol + 1; while Self.First <= Last loop Eol := Self.First; Self.Equal := 0; First_Non_WS := 0; Last_Non_WS := 0; -- Search end of current line and presence of '=' while Eol <= Last loop declare CC : constant Character := Self.Contents (Eol); begin case CC is when ASCII.LF => exit; when '=' => if Self.Equal = 0 then Self.Equal := Eol; end if; when ' ' | ASCII.CR | ASCII.HT | ASCII.VT | ASCII.FF => null; when others => if First_Non_WS = 0 then First_Non_WS := Eol; Last_Non_WS := Eol; else Last_Non_WS := Eol; end if; if Self.Equal = 0 and then CC = '=' then Self.Equal := Eol; end if; end case; end; Eol := Eol + 1; end loop; Self.Eol := Eol; if First_Non_WS = 0 then -- line containing only whitespaces null; elsif First_Non_WS + Comment - 1 <= Eol and then Slice (Self.Contents, First_Non_WS, First_Non_WS + Comment - 1) = Self.Comment_Start then -- This is comment line so skip it null; elsif Self.Equal /= 0 then -- We have an equal sign so this an assignement exit; elsif Self.Use_Sections and then Self.Contents (First_Non_WS) = '[' and then Self.Contents (Last_Non_WS) = ']' then -- This is a section declaration. Self.Current_Section := Self.Contents.Slice (First_Non_WS + 1, Last_Non_WS - 1); end if; Self.First := Eol + 1; end loop; end Next; --------------- -- Configure -- --------------- procedure Configure (Self : in out INI_Parser; Comment_Start : String := "#"; Handles_Sections : Boolean := True; Home : String := "") is begin Self.Comment_Start := To_XString (Comment_Start); Self.Use_Sections := Handles_Sections; if Home /= "" then Self.Home := Create (+Home); end if; end Configure; ---------------- -- Substitute -- ---------------- function Substitute (Self : INI_Parser'Class; Value : String) return String is function Callback (Name : String; Quoted : Boolean) return String; function Callback (Name : String; Quoted : Boolean) return String is pragma Unreferenced (Quoted); begin if Name = "HOME" then return +Self.Home.Full_Name; else raise Invalid_Substitution; end if; end Callback; begin return Substitute (Str => Value, Callback => Callback'Unrestricted_Access, Delimiter => '$'); exception when others => return Value; end Substitute; ------------- -- Section -- ------------- overriding function Section (Self : INI_Parser) return String is begin return Self.Current_Section.To_String; end Section; --------- -- Key -- --------- overriding function Key (Self : INI_Parser) return String is begin return Self.Contents.Slice (Self.First, Self.Equal - 1).Trim.To_String; end Key; ----------- -- Value -- ----------- overriding function Value (Self : INI_Parser) return String is begin return Substitute (Self, Trim (Self.Contents.Slice (Self.Equal + 1, Self.Eol - 1).To_String, Whitespaces, Whitespaces)); end Value; ---------- -- Fill -- ---------- procedure Fill (Self : in out Config_Pool; Config : in out Config_Parser'Class) is begin Set_System_Id (Self, To_String (Config.System_ID)); while not At_End (Config) loop Set (Self, Section (Config), Key (Config), Value (Config)); Next (Config); end loop; end Fill; ------------------- -- Set_System_Id -- ------------------- procedure Set_System_Id (Self : in out Config_Pool; System_ID : String) is begin Self.System_ID := To_XString (Normalize_Pathname (System_ID)); end Set_System_Id; ------------------ -- Internal_Get -- ------------------ function Internal_Get (Self : Config_Pool; Key : String; Section : String := Section_From_Key) return Config_Value is C : String_Maps.Cursor; begin if Section = Section_From_Key then for D in Key'Range loop if Key (D) = '.' then C := Self.Keys.Find (Key (Key'First .. D - 1) & '=' & Key (D + 1 .. Key'Last)); if C = No_Element then return No_Value; else return Element (C); end if; end if; end loop; C := Self.Keys.Find ('=' & Key); else C := Self.Keys.Find (Section & '=' & Key); end if; if C = No_Element then return No_Value; else return Element (C); end if; end Internal_Get; -------------- -- At_Index -- -------------- function At_Index (Value : Config_Value; Index : Natural := Whole_Value) return String is S : String_List_Access; begin if Index = Whole_Value then return Value.Value; else S := Split (Value.Value, ','); if Index > S'Last then Free (S); return ""; else return R : constant String := S (Index).all do Free (S); end return; end if; end if; end At_Index; --------- -- Get -- --------- function Get (Self : Config_Pool; Key : String; Section : String := Section_From_Key; Index : Natural := Whole_Value) return String is begin return At_Index (Internal_Get (Self, Key, Section), Index); end Get; ----------------- -- Get_Integer -- ----------------- function Get_Integer (Self : Config_Pool; Key : String; Section : String := Section_From_Key; Index : Natural := Whole_Value) return Integer is begin return Integer'Value (Get (Self, Key, Section, Index)); end Get_Integer; ----------------- -- Get_Boolean -- ----------------- function Get_Boolean (Self : Config_Pool; Key : String; Section : String := Section_From_Key; Index : Natural := Whole_Value) return Boolean is begin return Boolean'Value (Get (Self, Key, Section, Index)); end Get_Boolean; -------------- -- Get_File -- -------------- function Get_File (Self : Config_Pool; Key : String; Section : String := Section_From_Key; Index : Natural := Whole_Value) return String is Val : constant Config_Value := Internal_Get (Self, Key, Section); V : constant String := At_Index (Val, Index); begin if V = "" then return ""; elsif Is_Absolute_Path (V) then return V; else return Normalize_Pathname (V, To_String (Val.System_ID)); end if; end Get_File; ------------- -- To_File -- ------------- function To_File (Self : Config_Pool; Key : String; Section : String := Section_From_Key; Value : String) return Virtual_File is Val : constant Config_Value := Internal_Get (Self, Key, Section); begin if Value = "" then return GNATCOLL.VFS.No_File; elsif Is_Absolute_Path (Value) then return Create (+Value); else return Create (+Normalize_Pathname (Value, To_String (Val.System_ID))); end if; end To_File; --------- -- Set -- --------- procedure Set (Self : in out Config_Pool; Section, Key, Value : String) is begin Include (Self.Keys, Section & "=" & Key, Config_Value' (Len => Value'Length, Value => Value, System_ID => Self.System_ID)); end Set; ------------ -- Create -- ------------ function Create (Key : String; Section : String := "") return Config_Key is begin return Config_Key'(Section => To_XString (Section), Key => To_XString (Key)); end Create; --------- -- Get -- --------- function Get (Self : Config_Key; Conf : Config_Pool'Class; Index : Natural := Whole_Value) return String is begin return Get (Conf, To_String (Self.Key), To_String (Self.Section), Index); end Get; ----------------- -- Get_Integer -- ----------------- function Get_Integer (Self : Config_Key; Conf : Config_Pool'Class; Index : Natural := Whole_Value) return Integer is begin return Get_Integer (Conf, To_String (Self.Key), To_String (Self.Section), Index); end Get_Integer; ----------------- -- Get_Boolean -- ----------------- function Get_Boolean (Self : Config_Key; Conf : Config_Pool'Class; Index : Natural := Whole_Value) return Boolean is begin return Get_Boolean (Conf, To_String (Self.Key), To_String (Self.Section), Index); end Get_Boolean; -------------- -- Get_File -- -------------- function Get_File (Self : Config_Key; Conf : Config_Pool'Class; Index : Natural := Whole_Value) return String is begin return Get_File (Conf, To_String (Self.Key), To_String (Self.Section), Index); end Get_File; ------------- -- To_File -- ------------- function To_File (Self : Config_Key; Conf : Config_Pool'Class; Value : String) return Virtual_File is begin return To_File (Conf, To_String (Self.Key), To_String (Self.Section), Value); end To_File; end GNATCOLL.Config; gnatcoll-core-21.0.0/src/gnatcoll-scripts-shell.ads0000644000175000017500000004357113661715457022073 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package contains the implementation for a simple scripting language pragma Ada_2012; private with Ada.Containers.Indefinite_Doubly_Linked_Lists; private with Ada.Containers.Indefinite_Hashed_Maps; private with Ada.Strings.Hash; private with GNAT.Strings; package GNATCOLL.Scripts.Shell is Shell_Name : constant String := "shell"; type Shell_Scripting_Record is new Scripting_Language_Record with private; type Shell_Scripting is access all Shell_Scripting_Record'Class; type Shell_Callback_Data is new Callback_Data with private; type Shell_Class_Instance_Record is new Class_Instance_Record with private; type Shell_Subprogram_Record is new Subprogram_Record with private; type Shell_Subprogram is access all Shell_Subprogram_Record'Class; -- This types are declared in the spec rather than in the body, so that -- their subprograms can be overridden again. For instance, GPS uses that -- to make a subprogram_type be a GPS action rather than a simple shell -- command. procedure Register_Shell_Scripting (Repo : Scripts_Repository; Script : Shell_Scripting := null); -- Register the scripting language. -- Script can be specified if you want to specialize some aspects of the -- scripting language procedure Initialize (Data : in out Shell_Callback_Data'Class; Script : access Shell_Scripting_Record'Class); -- Initialize Data to pass Arguments_Count to a callback procedure List_Commands (Script : access Shell_Scripting_Record'Class; Console : Virtual_Console := null); -- Print the list of all commands on Console. By default, print on the -- default console for Script procedure Initialize (Subprogram : in out Shell_Subprogram_Record'Class; Script : access Scripting_Language_Record'Class; Command : String); -- Initialize Subprogram so that it will execute Command function Get_Command (Subprogram : access Shell_Subprogram_Record) return String; -- Return the command that will be executed by Subprogram function Get_Args (Data : Shell_Callback_Data) return GNAT.OS_Lib.Argument_List; -- Return the list of arguments specified by Data. The returned value must -- never be freed by the caller procedure Set_Prompt (Script : access Shell_Scripting_Record'Class; Prompt : String); -- The prompt to use for consoles associated with this language private Num_Previous_Returns : constant := 9; -- Number of parameters %1, %2,... which are used to memorize the result of -- previous commands. type Shell_Class_Instance_Record is new Class_Instance_Record with record Class : Class_Type; Props : aliased User_Data_List; end record; type Shell_Class_Instance is access all Shell_Class_Instance_Record'Class; overriding function Print_Refcount (Instance : access Shell_Class_Instance_Record) return String; overriding function Is_Subclass (Instance : access Shell_Class_Instance_Record; Base : String) return Boolean; overriding procedure Set_Property (Instance : access Shell_Class_Instance_Record; Name : String; Value : Integer); overriding procedure Set_Property (Instance : access Shell_Class_Instance_Record; Name : String; Value : Float); overriding procedure Set_Property (Instance : access Shell_Class_Instance_Record; Name : String; Value : Boolean); overriding procedure Set_Property (Instance : access Shell_Class_Instance_Record; Name : String; Value : String); overriding function Get_Method (Instance : access Shell_Class_Instance_Record; Name : String) return Subprogram_Type; overriding function Get_User_Data (Self : not null access Shell_Class_Instance_Record) return access User_Data_List; -- See doc from inherited subprogram package Instances_List is new Ada.Containers.Indefinite_Doubly_Linked_Lists (Class_Instance); -- ??? Would be faster to use a hash-table... ------------------------- -- Command_Information -- ------------------------- type Command_Information is record Command : GNAT.Strings.String_Access; Cmd : Command_Descr_Access; end record; type Command_Information_Access is access Command_Information; -- Description for each of the registered commands. -- Command is the name that must be typed by the user in the console. -- Short_Command is the name under which the command was registered. It is -- the same as Command, except when the command is a method of a class. In -- this case, Command is equal to "Class.Short_Command" -- The command was set as a constructor if Short_Command is -- Constructor_Method. procedure Free (Com : in out Command_Information_Access); -- Free memory associated with Com package Command_Hash is new Ada.Containers.Indefinite_Hashed_Maps (String, Command_Information_Access, Ada.Strings.Hash, "="); type Shell_Scripting_Record is new Scripting_Language_Record with record Repo : Scripts_Repository; Finalized : Boolean := False; Blocked : Boolean := False; Instances : Instances_List.List; -- All the instances that were created Commands_List : Command_Hash.Map; -- The list of all registered commands Returns : GNAT.Strings.String_List (1 .. Num_Previous_Returns); -- The result of the Num_Previous_Returns previous commands Prompt : GNAT.Strings.String_Access := new String'("[Shell]>"); -- Prompt to use in consoles for this language end record; overriding function Command_Line_Treatment (Script : access Shell_Scripting_Record) return Command_Line_Mode; overriding procedure Destroy (Script : access Shell_Scripting_Record); overriding procedure Register_Command (Script : access Shell_Scripting_Record; Command : Command_Descr_Access); overriding procedure Register_Property (Script : access Shell_Scripting_Record; Prop : Property_Descr_Access); overriding procedure Register_Class (Script : access Shell_Scripting_Record; Name : String; Base : Class_Type := No_Class; Module : Module_Type := Default_Module); overriding procedure Block_Commands (Script : access Shell_Scripting_Record; Block : Boolean); overriding procedure Execute_Command (Script : access Shell_Scripting_Record; CL : Arg_List; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : out Boolean); overriding function Execute_Command (Script : access Shell_Scripting_Record; CL : Arg_List; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : access Boolean) return String; overriding function Execute_Command (Script : access Shell_Scripting_Record; CL : Arg_List; Console : Virtual_Console := null; Hide_Output : Boolean := False; Errors : access Boolean) return Boolean; overriding function Execute_Command (Script : access Shell_Scripting_Record; Command : String; Args : Callback_Data'Class) return Boolean; overriding function Execute_Command_With_Args (Script : access Shell_Scripting_Record; CL : Arg_List) return String; overriding procedure Execute_File (Script : access Shell_Scripting_Record; Filename : String; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : out Boolean); overriding function Get_Name (Script : access Shell_Scripting_Record) return String; overriding function Get_Repository (Script : access Shell_Scripting_Record) return Scripts_Repository; overriding function Current_Script (Script : access Shell_Scripting_Record) return String; overriding procedure Display_Prompt (Script : access Shell_Scripting_Record; Console : Virtual_Console := null); overriding function Get_Prompt (Script : access Shell_Scripting_Record) return String; overriding procedure Complete (Script : access Shell_Scripting_Record; Input : String; Completions : out String_Lists.List); overriding function New_Instance (Script : access Shell_Scripting_Record; Class : Class_Type) return Class_Instance; overriding function New_List (Script : access Shell_Scripting_Record; Class : Class_Type := No_Class) return List_Instance'Class; -- See doc from inherited subprograms type Shell_Callback_Data is new Callback_Data with record Script : Shell_Scripting; CL : Arg_List; Return_Value : GNAT.Strings.String_Access; Return_Dict : GNAT.Strings.String_Access; Return_As_List : Boolean := False; Return_As_Error : Boolean := False; end record; overriding function Clone (Data : Shell_Callback_Data) return Callback_Data'Class; overriding function Get_Script (Data : Shell_Callback_Data) return Scripting_Language; overriding function Number_Of_Arguments (Data : Shell_Callback_Data) return Natural; overriding procedure Name_Parameters (Data : in out Shell_Callback_Data; Names : Cst_Argument_List); overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return String; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Unbounded_String; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Integer; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Float; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Boolean; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Subprogram_Type; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Class : Class_Type; Allow_Null : Boolean := False) return Class_Instance; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return List_Instance'Class; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Dictionary_Instance'Class; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Default : String) return String; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Default : Integer) return Integer; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Default : Float) return Float; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Default : Boolean) return Boolean; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Class : Class_Type := Any_Class; Default : Class_Instance; Allow_Null : Boolean := False) return Class_Instance; overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Default : Subprogram_Type) return Subprogram_Type; overriding procedure Set_Error_Msg (Data : in out Shell_Callback_Data; Msg : String); overriding procedure Set_Return_Value_As_List (Data : in out Shell_Callback_Data; Size : Natural := 0; Class : Class_Type := No_Class); overriding procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : Integer); overriding procedure Set_Address_Return_Value (Data : in out Shell_Callback_Data; Value : System.Address); overriding procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : Float); overriding procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : Boolean); overriding procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : String); overriding procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : Class_Instance); overriding procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : List_Instance); overriding procedure Set_Return_Value_Key (Data : in out Shell_Callback_Data; Key : String; Append : Boolean := False); overriding procedure Set_Return_Value_Key (Data : in out Shell_Callback_Data; Key : Integer; Append : Boolean := False); overriding procedure Set_Return_Value_Key (Data : in out Shell_Callback_Data; Key : Class_Instance; Append : Boolean := False); overriding procedure Free (Data : in out Shell_Callback_Data); overriding function Create (Script : access Shell_Scripting_Record; Arguments_Count : Natural) return Callback_Data'Class; overriding procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : String); overriding procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : Integer); overriding procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : Float); overriding procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : Boolean); overriding procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : Class_Instance); overriding procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : List_Instance); overriding procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : Subprogram_Type); overriding procedure Execute_Command (Args : in out Shell_Callback_Data; Command : String; Hide_Output : Boolean := True); overriding function Return_Value (Data : Shell_Callback_Data) return String; overriding function Return_Value (Data : Shell_Callback_Data) return Integer; overriding function Return_Value (Data : Shell_Callback_Data) return Float; overriding function Return_Value (Data : Shell_Callback_Data) return Boolean; overriding function Return_Value (Data : Shell_Callback_Data) return Class_Instance; overriding function Return_Value (Data : Shell_Callback_Data) return List_Instance'Class; overriding procedure Execute_Expression (Result : in out Shell_Callback_Data; Expression : String; Hide_Output : Boolean := True); -- See doc from inherited subprogram ---------------------- -- Shell_Subprogram -- ---------------------- type Shell_Subprogram_Record is new Subprogram_Record with record Script : Scripting_Language; Command : GNAT.Strings.String_Access; end record; -- subprograms in GPS shell are just GPS actions overriding function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return Boolean; overriding function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return String; overriding function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return Class_Instance; overriding function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return List_Instance'Class; overriding function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return GNAT.Strings.String_List; overriding function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return Any_Type; overriding procedure Free (Subprogram : in out Shell_Subprogram_Record); overriding function Get_Name (Subprogram : access Shell_Subprogram_Record) return String; overriding function Get_Script (Subprogram : Shell_Subprogram_Record) return Scripting_Language; -- See doc from inherited subprograms end GNATCOLL.Scripts.Shell; gnatcoll-core-21.0.0/src/gnatcoll-email-mailboxes.adb0000644000175000017500000007246213661715457022327 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Calendar; use Ada.Calendar; with Ada.Containers.Indefinite_Hashed_Maps; with Ada.IO_Exceptions; with Ada.Strings.Hash; with Ada.Unchecked_Deallocation; with GNATCOLL.Boyer_Moore; use GNATCOLL.Boyer_Moore; with GNATCOLL.Email.Parser; use GNATCOLL.Email.Parser; with GNATCOLL.Email.Utils; use GNATCOLL.Email.Utils; with GNATCOLL.Mmap; use GNATCOLL.Mmap; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNAT.Strings; use GNAT.Strings; package body GNATCOLL.Email.Mailboxes is use Message_Info_List, Cursor_List; From_Pattern : GNATCOLL.Boyer_Moore.Pattern; -- An efficient search pattern for the From_ lines that separate messages -- in a mbox procedure Internal_Search_Start (Self : Mbox'Class; Buffer : String; From : in out Integer); -- Search the start of the next message after the position From. -- It should return -1 if no further message exists. -- This is for the implementation of new mailbox types only, and is not -- needed when using standard mailboxes. function Earlier_Than (Left, Right : Abstract_Message_Info'Class) return Boolean; -- Compare two elements by date type Container; type Container_Access is access Container; type Container is record Msg : Message; Parent : Container_Access; Child : Container_Access; Next : Container_Access; end record; package Container_Hash is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => String, Element_Type => Container_Access, Hash => Ada.Strings.Hash, Equivalent_Keys => "="); use Container_Hash; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Container, Container_Access); ------------------ -- Earlier_Than -- ------------------ function Earlier_Than (Left, Right : Abstract_Message_Info'Class) return Boolean is Date1 : constant Time := Get_Date (Left.Msg); Date2 : constant Time := Get_Date (Right.Msg); begin return Date1 < Date2; end Earlier_Than; package Pkg_Sort_By_Date is new Message_Info_List.Generic_Sorting ("<" => Earlier_Than); ----------------- -- Free_String -- ----------------- procedure Free_String (Str : in out GNAT.Strings.String_Access) is begin GNAT.Strings.Free (Str); end Free_String; ---------------- -- Set_Parser -- ---------------- procedure Set_Parser (Self : in out Cursor; Factory : Message_Factory := Email.Parser.Parse'Access) is begin Self.Factory := Factory; end Set_Parser; ---------- -- Open -- ---------- procedure Open (Self : in out Mbox; Fp : access String; On_Close : Destructor := Free_String'Access) is begin Self.Fp := GNAT.Strings.String_Access (Fp); Self.On_Close := On_Close; end Open; ---------- -- Open -- ---------- procedure Open (Self : in out Mbox; Filename : Virtual_File) is begin Self.Fp := Read_File (Filename); Self.On_Close := Free_String'Access; if Self.Fp = null then raise Ada.IO_Exceptions.Name_Error with Filename.Display_Full_Name; end if; end Open; ----------- -- First -- ----------- overriding function First (Self : Mbox) return Cursor'Class is begin declare Cur : Cursor'Class := Mbox_Cursor' (Cursor with Max => Self.Fp'Last, Start => Self.Fp'First, Stop => 0, Current => Null_Message); begin Next (Cur, Self); return Cur; end; end First; ----------------- -- Has_Element -- ----------------- overriding function Has_Element (Self : Mbox_Cursor) return Boolean is begin return Self.Stop <= Self.Max; end Has_Element; ----------------- -- Get_Message -- ----------------- overriding procedure Get_Message (Self : in out Mbox_Cursor; Box : Mailbox'Class; Msg : out Message) is Buffer : Str_Access; begin -- Already cached ? if Self.Current /= Null_Message then Msg := Self.Current; -- Already past the end ? elsif Self.Stop > Self.Max or else Self.Factory = null then Msg := Null_Message; else if Mbox (Box).Fp /= null then Buffer := GNATCOLL.Mmap.Short.To_Str_Access (Mbox (Box).Fp); end if; Self.Factory (String (Buffer (Self.Start .. Self.Stop)), Self.Current); Msg := Self.Current; end if; end Get_Message; ---------- -- Next -- ---------- overriding procedure Next (Self : in out Mbox_Cursor; Box : Mailbox'Class) is Buffer : Str_Access; First : Integer; Skip_Separating_Newline : constant Integer := 2; -- Number of characters to move backward from a "From_" substring to -- find the end of the previous message. This includes skipping over the -- newline character that must separate messages. begin Self.Current := Null_Message; -- Already past the end ? if Self.Stop >= Self.Max then Self.Stop := Self.Max + 1; return; end if; if Mbox (Box).Fp /= null then Buffer := GNATCOLL.Mmap.Short.To_Str_Access (Mbox (Box).Fp); First := Mbox (Box).Fp'First; else return; -- Nothing to parse end if; -- Find start of first message if needed. If we are past this first -- message, we know we left the pointer to the start of a message, no -- need to check again. if Self.Stop = 0 then if String (Buffer (First .. 5)) /= "From " then Self.Stop := First; Internal_Search_Start (Mbox'Class (Box), String (Buffer (First .. Self.Max)), Self.Stop); if Self.Stop = -1 then Self.Stop := Integer'Last; Self.Current := Null_Message; return; else Self.Stop := Self.Stop - Skip_Separating_Newline; end if; else Self.Stop := First - Skip_Separating_Newline; end if; end if; -- At this point Self.Stop points to the first character before the -- beginning of the next message if Self.Stop >= Self.Max then return; end if; Self.Start := Self.Stop + Skip_Separating_Newline; -- Search end of message -- Skip current From_ line Self.Stop := Self.Start + 5; -- "From_"'Length Internal_Search_Start (Mbox'Class (Box), String (Buffer (Self.Stop .. Self.Max)), Self.Stop); if Self.Stop < 0 then -- No message after this one => it extends till the end of the buffer Self.Stop := Self.Max; else Self.Stop := Self.Stop - Skip_Separating_Newline; end if; end Next; --------------------------- -- Internal_Search_Start -- --------------------------- procedure Internal_Search_Start (Self : Mbox'Class; Buffer : String; From : in out Integer) is pragma Unreferenced (Self); begin -- Note: we used to treat a From_ line that does not contain a colon -- as part of the message body. This was not the intent of the code, -- and in any case was a questionable heuristic, since all From_ lines -- in bodies should really be assumed to have been escaped as >From_. -- So, we now just search for From_ at beginning of line. -- put in place a heuristic From := Search (From_Pattern, Buffer (From .. Buffer'Last)); if From /= -1 then -- Match present: skip initial ASCII.LF From := From + 1; end if; end Internal_Search_Start; -------------- -- Finalize -- -------------- overriding procedure Finalize (Self : in out Mailbox) is pragma Unreferenced (Self); begin null; end Finalize; -------------- -- Finalize -- -------------- overriding procedure Finalize (Self : in out Mbox) is begin if Self.On_Close /= null and then Self.Fp /= null then Self.On_Close (Self.Fp); Self.Fp := null; end if; end Finalize; ----------- -- Store -- ----------- procedure Store (Self : out Stored_Mailbox; Box : in out Mailbox'Class; Factory : Message_Factory := Email.Parser.Parse'Access) is begin Store (Self, Box, Factory, First (Box)); end Store; ----------- -- Store -- ----------- procedure Store (Self : out Stored_Mailbox; Box : in out Mailbox'Class; Factory : Message_Factory := Email.Parser.Parse'Access; From : Cursor'Class) is Msg : Message; Curs : Cursor'Class := From; begin Set_Parser (Curs, Factory); while Has_Element (Curs) loop Get_Message (Curs, Box, Msg); if Msg /= Null_Message then Append (Self, Msg); end if; Next (Curs, Box); end loop; end Store; ------------ -- Append -- ------------ procedure Append (Self : in out Stored_Mailbox; Msg : Message) is begin Append (Self.Messages, Message_Info'(Msg => Msg, Children => Message_Info_List.Empty_List)); Self.Sorted_By := Sort_None; end Append; ----------- -- First -- ----------- overriding function First (Self : Stored_Mailbox) return Cursor'Class is begin return First (Self, Recurse => False); end First; ----------- -- First -- ----------- function First (Self : Stored_Mailbox; Recurse : Boolean) return Stored_Mailbox_Cursor'Class is L : Stored_Mailbox_Cursor; begin L.Recurse := Recurse; L.Thread_Level := 1; if not Is_Empty (Self.Messages) then Append (L.Cursors, First (Self.Messages)); end if; return L; end First; --------------------- -- First_In_Thread -- --------------------- function First_In_Thread (Self : Stored_Mailbox; Parent : Stored_Mailbox_Cursor'Class) return Stored_Mailbox_Cursor'Class is L : Stored_Mailbox_Cursor; procedure Get_First (M : Abstract_Message_Info'Class); procedure Get_First (M : Abstract_Message_Info'Class) is begin if Has_Element (First (Message_Info (M).Children)) then Append (L.Cursors, First (Message_Info (M).Children)); else L.Cursors := Cursor_List.Empty_List; end if; end Get_First; begin if Self.Threaded and then not Is_Empty (Parent.Cursors) and then Has_Element (Element (Last (Parent.Cursors))) then L.Recurse := False; L.Thread_Level := Parent.Thread_Level + 1; Query_Element (Element (Last (Parent.Cursors)), Get_First'Unrestricted_Access); return L; else L.Cursors := Cursor_List.Empty_List; return L; end if; end First_In_Thread; ----------------- -- Has_Element -- ----------------- overriding function Has_Element (Self : Stored_Mailbox_Cursor) return Boolean is begin return not Is_Empty (Self.Cursors); end Has_Element; ----------------- -- Get_Message -- ----------------- overriding procedure Get_Message (Self : in out Stored_Mailbox_Cursor; Box : Mailbox'Class; Msg : out Message) is pragma Unreferenced (Box); Saved : Message_Info_List.Cursor; begin if Is_Empty (Self.Cursors) then Msg := Null_Message; else Saved := Element (Last (Self.Cursors)); Msg := Message_Info (Element (Saved)).Msg; end if; end Get_Message; ---------------------- -- Get_Thread_Level -- ---------------------- function Get_Thread_Level (Iter : Stored_Mailbox_Cursor) return Positive is begin if Is_Empty (Iter.Cursors) then return 1; else return Iter.Thread_Level; end if; end Get_Thread_Level; ---------- -- Next -- ---------- overriding procedure Next (Self : in out Stored_Mailbox_Cursor; Box : Mailbox'Class) is pragma Unreferenced (Box); M : Message_Info; Saved : Message_Info_List.Cursor; procedure Move_To_Next (C : in out Message_Info_List.Cursor); procedure Move_To_Next (C : in out Message_Info_List.Cursor) is begin Next (C); end Move_To_Next; begin if not Is_Empty (Self.Cursors) then Saved := Element (Last (Self.Cursors)); M := Message_Info (Element (Saved)); -- If we have children for the current element, return these next if Self.Recurse and then not Is_Empty (M.Children) then declare procedure Get_First (M : Abstract_Message_Info'Class); procedure Get_First (M : Abstract_Message_Info'Class) is begin Append (Self.Cursors, First (Message_Info (M).Children)); end Get_First; begin Self.Thread_Level := Self.Thread_Level + 1; Query_Element (Saved, Get_First'Unrestricted_Access); end; else -- Otherwise move to the next element at the same level if there's -- one. Or move to the next element at the parent level, -- recursively Update_Element (Self.Cursors, Last (Self.Cursors), Move_To_Next'Unrestricted_Access); if Self.Recurse then while not Has_Element (Element (Last (Self.Cursors))) loop -- Else move to the next element at the parent level, -- recursively Delete_Last (Self.Cursors); exit when Is_Empty (Self.Cursors); Self.Thread_Level := Self.Thread_Level - 1; Update_Element (Self.Cursors, Last (Self.Cursors), Move_To_Next'Unrestricted_Access); end loop; elsif not Has_Element (Element (Last (Self.Cursors))) then Self.Cursors := Cursor_List.Empty_List; end if; end if; end if; end Next; -------------------- -- Remove_Threads -- -------------------- procedure Remove_Threads (Stored : in out Stored_Mailbox) is begin if Stored.Threaded then declare Iter : Stored_Mailbox_Cursor'Class := First (Stored, Recurse => True); Tmp : Message_Info_List.List; Msg : Message; begin while Has_Element (Iter) loop Get_Message (Iter, Stored, Msg); if Msg /= Null_Message then Append (Tmp, Message_Info'(Msg => Msg, Children => Message_Info_List.Empty_List)); end if; Next (Iter, Stored); end loop; Move (Target => Stored.Messages, Source => Tmp); Stored.Threaded := False; Stored.Sorted_By := Sort_None; end; end if; end Remove_Threads; ------------------ -- Sort_By_Date -- ------------------ procedure Sort_By_Date (Self : in out Stored_Mailbox) is procedure Sort_Level (List : in out Message_Info_List.List); procedure Sort_Info (Info : in out Abstract_Message_Info'Class); -- Sort List, and all children procedure Sort_Info (Info : in out Abstract_Message_Info'Class) is begin Sort_Level (Message_Info (Info).Children); end Sort_Info; procedure Sort_Level (List : in out Message_Info_List.List) is C : Message_Info_List.Cursor; begin Pkg_Sort_By_Date.Sort (List); C := First (List); while Has_Element (C) loop if not Is_Empty (Message_Info (Element (C)).Children) then Update_Element (List, C, Sort_Info'Unrestricted_Access); end if; Next (C); end loop; end Sort_Level; begin if Self.Sorted_By /= Sort_Date then if Self.Threaded then Sort_Level (Self.Messages); else Pkg_Sort_By_Date.Sort (Self.Messages); end if; Self.Sorted_By := Sort_Date; end if; end Sort_By_Date; --------------------- -- Thread_Messages -- --------------------- procedure Thread_Messages (Self : in out Stored_Mailbox) is Ids : Container_Hash.Map; procedure Store_Parent_Of (Parent_Cont : in out Container_Access; Id : String); -- Memorize that Parent_Cont is the parent of the message whose -- Message-ID is Id. -- On exit, Parent_Cont is set to the container used for the message -- itself. procedure Set_Parent_For_All_Ids (Parent_Cont : in out Container_Access; H : Header); -- For each message-id found in H, call Store_Parent_Of. This -- sets a list of dependencies between mail messages. procedure Set_Parent_Of (Cont : Container_Access; Parent_Cont : Container_Access; Override : Boolean := False); -- Set the parent of Cont to be Parent_Cont. -- If Cont already has a parent and Override is True, it is replaced. -- This is only needed when processing the last item in the References: -- field, which we know is the real parent. Previous parents might have -- been set while processing References: fields in other messages. -- This also update child links procedure Put_Threads_In_Message (Parent : in out Message_Info_List.List; Root : Container_Access; Root_Level : Boolean); -- Put Root in Parent, and all its children recursively function Is_Reachable (A, B : Container_Access) return Boolean; -- Return True if B is part of the descendant of A. This doesn't test -- that A /= B. ------------------ -- Is_Reachable -- ------------------ function Is_Reachable (A, B : Container_Access) return Boolean is C : Container_Access := A.Child; C2 : Container_Access; begin if A = B then return True; end if; while C /= null loop if C = B then return True; end if; C2 := C.Child; while C2 /= null loop if Is_Reachable (C2, B) then return True; end if; C2 := C2.Next; end loop; C := C.Next; end loop; return False; end Is_Reachable; ------------------- -- Set_Parent_Of -- ------------------- procedure Set_Parent_Of (Cont : Container_Access; Parent_Cont : Container_Access; Override : Boolean := False) is C : Container_Access; begin if Parent_Cont = null or else Parent_Cont = Cont then return; end if; if Cont.Parent /= null and then not Override then return; end if; -- Check we do not introduce a loop A -> B -> A. This must be done -- before removing the old parent if Is_Reachable (Cont, Parent_Cont) or else Is_Reachable (Parent_Cont, Cont) then return; end if; -- Set the new parent (remove the old one, since if we reach here -- with an old parent, we are in Override mode) if Cont.Parent /= null then if Cont.Parent.Child = Cont then Cont.Parent.Child := Cont.Next; else C := Cont.Parent.Child; while C.Next /= null loop if C.Next = Cont then C.Next := Cont.Next; exit; end if; C := C.Next; end loop; end if; Cont.Parent := null; Cont.Next := null; end if; Cont.Parent := Parent_Cont; if Parent_Cont /= null then if Parent_Cont.Child = null then Parent_Cont.Child := Cont; else C := Parent_Cont.Child; while C.Next /= null loop C := C.Next; end loop; C.Next := Cont; end if; end if; Cont.Next := null; end Set_Parent_Of; --------------------- -- Store_Parent_Of -- --------------------- procedure Store_Parent_Of (Parent_Cont : in out Container_Access; Id : String) is Id_C : Container_Hash.Cursor; Cont : Container_Access; begin Id_C := Find (Ids, Id); if Has_Element (Id_C) then Cont := Element (Id_C); else Cont := new Container; -- Null_Message; Insert (Ids, Id, Cont); end if; if Parent_Cont /= null and then Cont.Parent = null then Set_Parent_Of (Cont => Cont, Parent_Cont => Parent_Cont); end if; Parent_Cont := Cont; end Store_Parent_Of; ---------------------------- -- Set_Parent_For_All_Ids -- ---------------------------- procedure Set_Parent_For_All_Ids (Parent_Cont : in out Container_Access; H : Header) is Flat : Unbounded_String; Index : Natural; Stop : Natural; begin if H /= Null_Header then Flatten (Get_Value (H), Flat); declare StrA : constant String := To_String (Flat); begin Index := StrA'First; while Index <= StrA'Last loop Index := Next_Occurrence (StrA (Index .. StrA'Last), '<'); Stop := Next_Occurrence (StrA (Index + 1 .. StrA'Last), '>'); Store_Parent_Of (Parent_Cont => Parent_Cont, Id => StrA (Index + 1 .. Stop - 1)); Index := Stop + 1; end loop; end; end if; end Set_Parent_For_All_Ids; ---------------------------- -- Put_Threads_In_Message -- ---------------------------- procedure Put_Threads_In_Message (Parent : in out Message_Info_List.List; Root : Container_Access; Root_Level : Boolean) is C : Container_Access; M : Message_Info; begin -- Do not insert dummy containers if Root.Msg = Null_Message then if Root.Child = null then return; end if; -- Promote the children one level up C := Root.Child; while C /= null loop Put_Threads_In_Message (Parent, C, Root_Level); C := C.Next; end loop; return; else M.Msg := Root.Msg; Append (Parent, M); C := Root.Child; end if; while C /= null loop declare procedure Add_Child (MI : in out Abstract_Message_Info'Class); procedure Add_Child (MI : in out Abstract_Message_Info'Class) is begin Put_Threads_In_Message (Message_Info (MI).Children, C, False); end Add_Child; begin Update_Element (Parent, Last (Parent), Add_Child'Unrestricted_Access); end; C := C.Next; end loop; end Put_Threads_In_Message; C : Message_Info_List.Cursor; Msg : Message; Msg_Cont : Container_Access; Parent_Cont : Container_Access; Id_C : Container_Hash.Cursor; Dummy_Id : Natural := 0; -- For those messages that have no Message-Id: field, so that we don't -- lose any... begin -- See algorithm at http://www.jwz.org/doc/threading.html if not Self.Threaded then C := First (Self.Messages); while Has_Element (C) loop Msg := Element (C).Msg; -- Store a new container for this message-id in the table declare Id : constant String := Get_Message_Id (Msg); begin Msg_Cont := null; Store_Parent_Of (Parent_Cont => Msg_Cont, Id => Id); -- If there was already such a message-id (in theory not -- possible, in practice we might have messages with no -- Message-Id: header, and we still want to keep all messages. -- We create a dummy, unique, invalid ID containing a space. while Msg_Cont.Msg /= Null_Message loop Msg_Cont := null; Store_Parent_Of (Parent_Cont => Msg_Cont, Id => Id & Integer'Image (Dummy_Id)); Dummy_Id := Dummy_Id + 1; end loop; end; Msg_Cont.Msg := Element (C).Msg; -- For each of the References: id: Parent_Cont := null; Set_Parent_For_All_Ids (Parent_Cont => Parent_Cont, H => Get_Header (Msg, "References")); -- Take into account the In-Reply-To field. In most cases, this -- should duplicate the last element of References:, but some -- mailers do not behave correctly, so it doesn't harm to -- do the work again Set_Parent_For_All_Ids (Parent_Cont => Parent_Cont, H => Get_Header (Msg, "In-Reply-To")); Set_Parent_Of (Msg_Cont, Parent_Cont => Parent_Cont, Override => True); Next (C); end loop; -- Prune empty containers. These come from invalid references in -- the headers -- ??? Nothing do do in fact, we'll just ignore these when putting -- messages back in Self.Messages. -- We would also promote the child of empty containers with one child -- and no parent to the root set. -- Find all elements of the root set -- ??? Not needed, we do it when we put messages back in -- Self.Messages -- Sort messages by Subject, to find additional threads -- ??? Disabled for now Clear (Self.Messages); Id_C := First (Ids); while Has_Element (Id_C) loop if Element (Id_C).Parent = null then Put_Threads_In_Message (Parent => Self.Messages, Root => Element (Id_C), Root_Level => True); end if; Next (Id_C); end loop; -- Free memory Id_C := First (Ids); while Has_Element (Id_C) loop Msg_Cont := Element (Id_C); Unchecked_Free (Msg_Cont); Next (Id_C); end loop; Self.Threaded := True; case Self.Sorted_By is when Sort_None => null; when Sort_Date => Self.Sorted_By := Sort_None; Sort_By_Date (Self); end case; end if; end Thread_Messages; ----------------- -- Is_Threaded -- ----------------- function Is_Threaded (Self : Stored_Mailbox) return Boolean is begin return Self.Threaded; end Is_Threaded; begin -- Can't include two ASCII.LF, since officially mboxes should even use -- ASCII.CR & ASCII.LF, although most don't Compile (From_Pattern, ASCII.LF & "From "); end GNATCOLL.Email.Mailboxes; gnatcoll-core-21.0.0/src/gnatcoll-mmap.adb0000644000175000017500000004004313661715457020177 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2007-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.IO_Exceptions; with Ada.Unchecked_Deallocation; with GNATCOLL.Mmap.System; use GNATCOLL.Mmap.System; with GNATCOLL.Strings; use GNATCOLL.Strings; with System; use System; with System.Storage_Elements; use System.Storage_Elements; package body GNATCOLL.Mmap is type Mapped_File_Record is record Current_Region : Mapped_Region; -- The legacy API enables only one region to be mapped, directly -- associated with the mapped file. This references this region. File : System_File; -- Underlying OS-level file end record; type Mapped_Region_Record is record File : Mapped_File; -- The file this region comes from. Be careful: for reading file, it is -- valid to have it closed before one of its regions is free'd. Write : Boolean; -- Whether the file this region comes from is open for writing. Data : Standard.System.Address := Standard.System.Null_Address; -- Unbounded access to the mapped content. System_Offset : File_Size; -- Position in the file of the first byte actually mapped in memory User_Offset : File_Size; -- Position in the file of the first byte requested by the user System_Size : File_Size; -- Size of the region actually mapped in memory User_Size : File_Size; -- Size of the region requested by the user Mapped : Boolean; -- Whether this region is actually memory mapped Mutable : Boolean; -- If the file is opened for reading, wheter this region is writable Buffer : GNAT.Strings.String_Access; -- When this region is not actually memory mapped, contains the -- requested bytes. Mapping : System_Mapping; -- Underlying OS-level data for the mapping, if any end record; Invalid_Mapped_Region_Record : constant Mapped_Region_Record := (null, False, Standard.System.Null_Address, 0, 0, 0, 0, False, False, null, Invalid_System_Mapping); Invalid_Mapped_File_Record : constant Mapped_File_Record := (Invalid_Mapped_Region, Invalid_System_File); Empty_String : constant String := ""; -- Used to provide a valid empty Data for empty files, for instanc. procedure Dispose is new Ada.Unchecked_Deallocation (Mapped_File_Record, Mapped_File); procedure Dispose is new Ada.Unchecked_Deallocation (Mapped_Region_Record, Mapped_Region); procedure Compute_Data (Region : Mapped_Region); -- Fill the Data field according to system and user offsets. The region -- must actually be mapped or bufferized. procedure From_Disk (Region : Mapped_Region); -- Read a region of some file from the disk. -- Doesn't use the mmap system call. procedure To_Disk (Region : Mapped_Region); -- Write the region of the file back to disk if necessary, and free memory --------------- -- Open_Read -- --------------- function Open_Read (Filename : String; Use_Mmap_If_Available : Boolean := True) return Mapped_File is File : constant System_File := Open_Read (Filename, Use_Mmap_If_Available); begin return new Mapped_File_Record' (Current_Region => Invalid_Mapped_Region, File => File); end Open_Read; ---------------- -- Open_Write -- ---------------- function Open_Write (Filename : String; Use_Mmap_If_Available : Boolean := True) return Mapped_File is File : constant System_File := Open_Write (Filename, Use_Mmap_If_Available); begin return new Mapped_File_Record' (Current_Region => Invalid_Mapped_Region, File => File); end Open_Write; ----------- -- Close -- ----------- procedure Close (File : in out Mapped_File) is begin -- Closing a closed file is allowed and should do nothing if File = Invalid_Mapped_File then return; end if; if File.Current_Region /= null then Free (File.Current_Region); end if; if File.File /= Invalid_System_File then Close (File.File); end if; Dispose (File); end Close; ---------- -- Free -- ---------- procedure Free (Region : in out Mapped_Region) is Ignored : Integer; pragma Unreferenced (Ignored); begin -- Freeing an already free'd file is allowed and should do nothing if Region = Invalid_Mapped_Region then return; end if; if Region.Mapping /= Invalid_System_Mapping then Dispose_Mapping (Region.Mapping); end if; To_Disk (Region); Dispose (Region); end Free; ---------- -- Read -- ---------- procedure Read (File : Mapped_File; Region : in out Mapped_Region; Offset : File_Size := 0; Length : File_Size := 0; Mutable : Boolean := False; Advice : Use_Advice := Use_Normal) is File_Length : constant File_Size := GNATCOLL.Mmap.Length (File); Req_Offset : constant File_Size := Offset; Req_Length : File_Size := Length; -- Offset and Length of the region to map, used to adjust mapping -- bounds, reflecting what the user will see. Region_Allocated : Boolean := False; begin -- If this region comes from another file, or simply if the file is -- writeable, we cannot re-use this mapping: free it first. if Region /= Invalid_Mapped_Region and then (Region.File /= File or else File.File.Write) then Free (Region); end if; if Region = Invalid_Mapped_Region then Region := new Mapped_Region_Record'(Invalid_Mapped_Region_Record); Region_Allocated := True; end if; Region.File := File; if Req_Offset >= File_Length then -- If the requested offset goes beyond file size, map nothing Req_Length := 0; elsif Length = 0 or else Length > File_Length - Req_Offset then -- If Length is 0 or goes beyond file size, map till end of file Req_Length := File_Length - Req_Offset; else Req_Length := Length; end if; -- Past this point, the offset/length the user will see is fixed. On the -- other hand, the system offset/length is either already defined, from -- a previous mapping, or it is set to 0. In the latter case, the next -- step will set them according to the mapping. Region.User_Offset := Req_Offset; Region.User_Size := Req_Length; -- If the requested region is inside an already mapped region, adjust -- user-requested data and do nothing else. if (File.File.Write or else Region.Mutable = Mutable) and then Req_Offset >= Region.System_Offset and then (Req_Offset + Req_Length <= Region.System_Offset + Region.System_Size) then Region.User_Offset := Req_Offset; Compute_Data (Region); return; elsif Region.Buffer /= null then -- Otherwise, as we are not going to re-use the buffer, free it GNAT.Strings.Free (Region.Buffer); Region.Buffer := null; elsif Region.Mapping /= Invalid_System_Mapping then -- Otherwise, there is a memory mapping that we need to unmap. Dispose_Mapping (Region.Mapping); end if; -- mmap() will sometimes return NULL when the file exists but is empty, -- which is not what we want, so in the case of a zero length file we -- fall back to read(2)/write(2)-based mode. if File_Length > 0 and then File.File.Mapped then Region.System_Offset := Req_Offset; Region.System_Size := Req_Length; Create_Mapping (File.File, Region.System_Offset, Region.System_Size, Mutable, Region.Mapping, Advice); Region.Mapped := True; Region.Mutable := Mutable; else -- There is no alignment requirement when manually reading the file. Region.System_Offset := Req_Offset; Region.System_Size := Req_Length; Region.Mapped := False; Region.Mutable := True; From_Disk (Region); end if; Region.Write := File.File.Write; Compute_Data (Region); exception when others => -- Before propagating any exception, free any region we allocated -- here. if Region_Allocated then Dispose (Region); end if; raise; end Read; ---------- -- Read -- ---------- procedure Read (File : Mapped_File; Offset : File_Size := 0; Length : File_Size := 0; Mutable : Boolean := False) is begin Read (File, File.Current_Region, Offset, Length, Mutable); end Read; ---------- -- Read -- ---------- function Read (File : Mapped_File; Offset : File_Size := 0; Length : File_Size := 0; Mutable : Boolean := False; Advice : Use_Advice := Use_Normal) return Mapped_Region is Region : Mapped_Region := Invalid_Mapped_Region; begin Read (File, Region, Offset, Length, Mutable, Advice); return Region; end Read; ------------ -- Length -- ------------ function Length (File : Mapped_File) return File_Size is begin return File.File.Length; end Length; ------------ -- Offset -- ------------ function Offset (Region : Mapped_Region) return File_Size is begin return Region.User_Offset; end Offset; ------------ -- Offset -- ------------ function Offset (File : Mapped_File) return File_Size is begin return Offset (File.Current_Region); end Offset; --------------- -- Data_Size -- --------------- function Data_Size (Region : Mapped_Region) return File_Size is begin return Region.User_Size; end Data_Size; --------------- -- Data_Size -- --------------- function Data_Size (File : Mapped_File) return File_Size is begin return Data_Size (File.Current_Region); end Data_Size; ------------------ -- Data_Address -- ------------------ function Data_Address (Region : Mapped_Region) return Standard.System.Address is begin return Region.Data; end Data_Address; ------------------ -- Data_Address -- ------------------ function Data_Address (File : Mapped_File) return Standard.System.Address is begin return Data_Address (File.Current_Region); end Data_Address; ---------------- -- Is_Mutable -- ---------------- function Is_Mutable (Region : Mapped_Region) return Boolean is begin return Region.Mutable or Region.Write; end Is_Mutable; ---------------- -- Is_Mmapped -- ---------------- function Is_Mmapped (File : Mapped_File) return Boolean is begin return File.File.Mapped; end Is_Mmapped; ------------------- -- Get_Page_Size -- ------------------- function Get_Page_Size return Positive is Result : constant File_Size := Get_Page_Size; begin return Positive (Result); end Get_Page_Size; --------------------- -- Read_Whole_File -- --------------------- function Read_Whole_File (Filename : String; Empty_If_Not_Found : Boolean := False) return GNAT.Strings.String_Access is File : Mapped_File := Open_Read (Filename); Region : Mapped_Region renames File.Current_Region; Result : String_Access; begin Read (File); if Region.Data /= Standard.System.Null_Address then Result := new String' (String (Data (Region) (1 .. Integer (Last (Region))))); elsif Region.Buffer /= null then Result := Region.Buffer; Region.Buffer := null; -- So that it is not deallocated end if; Close (File); return Result; exception when Ada.IO_Exceptions.Name_Error => if Empty_If_Not_Found then return new String'(""); else return null; end if; when others => Close (File); return null; end Read_Whole_File; --------------------- -- Read_Whole_File -- --------------------- function Read_Whole_File (Filename : String) return GNATCOLL.Strings.XString is File : Mapped_File := Open_Read (Filename); Region : Mapped_Region renames File.Current_Region; Result : XString; begin Read (File); if Region.Data /= Standard.System.Null_Address then Result.Set (String (Data (Region) (1 .. Last (Region)))); elsif Region.Buffer /= null then Result.Set (Region.Buffer.all); end if; Close (File); return Result; exception when Ada.IO_Exceptions.Name_Error => return GNATCOLL.Strings.Null_XString; when others => Close (File); return GNATCOLL.Strings.Null_XString; end Read_Whole_File; --------------- -- From_Disk -- --------------- procedure From_Disk (Region : Mapped_Region) is begin pragma Assert (Region.File.all /= Invalid_Mapped_File_Record); pragma Assert (Region.Buffer = null); Region.Buffer := Read_From_Disk (Region.File.File, Region.User_Offset, Region.User_Size); Region.Mapped := False; end From_Disk; ------------- -- To_Disk -- ------------- procedure To_Disk (Region : Mapped_Region) is begin if Region.Write and then Region.Buffer /= null then pragma Assert (Region.File.all /= Invalid_Mapped_File_Record); Write_To_Disk (Region.File.File, Region.User_Offset, Region.User_Size, Region.Buffer); end if; GNAT.Strings.Free (Region.Buffer); Region.Buffer := null; end To_Disk; ------------------ -- Compute_Data -- ------------------ procedure Compute_Data (Region : Mapped_Region) is Data_Shift : constant Storage_Offset := Storage_Offset (Region.User_Offset - Region.System_Offset); begin if Region.User_Size = 0 then Region.Data := Empty_String'Address; elsif Region.Mapped then Region.Data := Region.Mapping.Address + Data_Shift; else Region.Data := Region.Buffer.all'Address + Data_Shift; end if; end Compute_Data; end GNATCOLL.Mmap; gnatcoll-core-21.0.0/src/gnatcoll-formatters.adb0000644000175000017500000001266013661715457021437 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package body GNATCOLL.Formatters is ---------------------- -- Columns_Vertical -- ---------------------- procedure Columns_Vertical (Words : Strings.XString_Array; Width : Positive; Put_Line : not null access procedure (Line : Strings.XString); Pad : Strings.Char_Type := Strings.Space; Delimiter : Strings.Char_String := (1 => Strings.Space)) is use Strings; Rows : Natural; Cols : Natural; Max : Natural := 0; Idx : Natural; Len : Natural; begin -- Prepare initial rough proposal for number of rows for W of Words loop if W.Length > Max then Max := W.Length; end if; end loop; Cols := Width / (Max + Delimiter'Length); Rows := Words'Length / Cols; if Words'Length rem Cols /= 0 then Rows := Rows + 1; end if; -- Trying to reduce number of rows using max length in each column Reduce_Rows : while Rows > 1 loop Rows := Rows - 1; Cols := Words'Length / Rows; if Words'Length rem Rows > 0 then Cols := Cols + 1; end if; Idx := Words'First; Len := 0; for Col in 1 .. Cols loop Max := 0; for Row in 1 .. Rows loop if Max < Words (Idx).Length then Max := Words (Idx).Length; end if; Idx := Idx + 1; exit when Idx > Words'Last; end loop; Len := Len + Max + Delimiter'Length; if Len > Width then Rows := Rows + 1; exit Reduce_Rows; end if; exit when Idx > Words'Last; end loop; end loop Reduce_Rows; Cols := Words'Length / Rows; if Words'Length rem Rows > 0 then Cols := Cols + 1; end if; declare Cmax : array (1 .. Cols) of Positive := (others => 1); Line : XString; Align : Natural; begin for J in Words'Range loop Idx := (J - Words'First) / Rows + 1; if Cmax (Idx) < Words (J).Length then Cmax (Idx) := Words (J).Length; end if; end loop; for Row in 1 .. Rows loop Line.Clear; Align := 0; for Col in 1 .. Cols loop Idx := (Col - 1) * Rows + Row - 1 + Words'First; exit when Idx > Words'Last; if not Line.Is_Empty then Line.Append (Align * Pad); Line.Append (Delimiter); end if; Line.Append (Words (Idx)); Align := Cmax (Col) - Words (Idx).Length; end loop; Put_Line (Line); end loop; end; end Columns_Vertical; ------------------------------- -- Columns_Vertical_XStrings -- ------------------------------- function Columns_Vertical_XString (Words : Strings.XString_Array; Width : Positive; Pad : Strings.Char_Type := Strings.Space; Delimiter : Strings.Char_String := (1 => Strings.Space)) return Strings.XString is Result : Strings.XString; procedure Append_Line (Line : Strings.XString); -- Append line to result --------------- -- Each_Line -- --------------- procedure Append_Line (Line : Strings.XString) is begin Result.Append (Line); Result.Append (End_Of_Line); end Append_Line; procedure Format_Columns is new Columns_Vertical (Strings); begin Format_Columns (Words, Width, Append_Line'Access, Pad, Delimiter); return Result; end Columns_Vertical_XString; end GNATCOLL.Formatters; gnatcoll-core-21.0.0/src/gnatcoll-scripts-impl.ads0000644000175000017500000000611313661715457021714 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides subprograms that are used when adding support for -- new scripting languages. Applications should not typically have a need for -- these types or subprograms. package GNATCOLL.Scripts.Impl is procedure Insert_Text (Script : access Scripting_Language_Record'Class; Console : Virtual_Console := null; Txt : String); procedure Insert_Error (Script : access Scripting_Language_Record'Class; Console : Virtual_Console := null; Txt : String); procedure Insert_Prompt (Script : access Scripting_Language_Record'Class; Console : Virtual_Console := null; Txt : String); -- Display Txt either on the specified console or on the scripts' default -- console if Console is set to null. -- If Hide is set to True, the text is not displayed on the console after -- all, although it will be displayed in the log instead. procedure Register_Console_Class (Repo : access Scripts_Repository_Record'Class; Class : Class_Type); -- Register the console class, which is used to redirect output of script -- languages to a specific Virtual_Console procedure Register_Logger_Class (Repo : access Scripts_Repository_Record'Class; Class : Class_Type); -- Register the logger class, used to interfaces with GNATCOLL.Traces from -- python. end GNATCOLL.Scripts.Impl; gnatcoll-core-21.0.0/src/separate_run_path_option.c0000644000175000017500000000351213661715457022234 0ustar nicolasnicolas/*---------------------------------------------------------------------------- -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -----------------------------------------------------------------------------*/ /* Dummy version of __gnat_separate_run_path_options, needed by mlib.adb */ const char __gnat_separate_run_path_options = 0; gnatcoll-core-21.0.0/src/gnatcoll-memory.ads0000644000175000017500000002652513661715457020607 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2005-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides a reimplementation of GNAT's low-level memory -- allocation mechanism. Its goal is to provide an additional monitoring -- facility to check where your program allocates memory. -- -- To activate this alternative implementation in your application, you -- must provide your own s-memory.adb somewhere in your source directories. -- Then simply recompile the modified body of that package with -- gnatmake -u -a -g s-memory.adb (or use the -a switch when you compile -- your own application) and make sure that the ali and object files for -- this unit are found in the object search path. -- -- Your version of System.Memory (in file s-memory.adb) should be: -- -- with GNATCOLL.Memory; -- package body System.Memory is -- package M renames GNATCOLL.Memory; -- -- function Alloc (Size : size_t) return System.Address is -- begin -- return M.Alloc (M.size_t (Size)); -- end Alloc; -- -- procedure Free (Ptr : System.Address) -- renames M.Free; -- -- function Realloc -- (Ptr : System.Address; -- Size : size_t) -- return System.Address is -- begin -- return M.Realloc (Ptr, M.size_t (Size)); -- end Realloc; -- end System.Memory; -- -- As a child package of System, this package must be compiled with -gnatg -- switch to the compiler. with System; use System; with GNAT.Debug_Pools; use GNAT.Debug_Pools; package GNATCOLL.Memory is type size_t is mod 2 ** Standard'Address_Size; -- Same as System.Memory.size_t, but defined here to avoid elaboration -- circularity issues function Alloc (Size : size_t) return System.Address; -- This is the low level allocation routine. Given a size in storage -- units, it returns the address of a maximally aligned block of -- memory. The implementation of this routine is guaranteed to be -- task safe, and also aborts are deferred if necessary. -- -- If size_t is set to size_t'Last on entry, then a Storage_Error -- exception is raised with a message "object too large". -- -- If size_t is set to zero on entry, then a minimal (but non-zero) -- size block is allocated. -- -- Note: this is roughly equivalent to the standard C malloc call -- with the additional semantics as described above. procedure Free (Ptr : System.Address); -- This is the low level free routine. It frees a block previously -- allocated with a call to Alloc. As in the case of Alloc, this -- call is guaranteed task safe, and aborts are deferred. -- -- Note: this is roughly equivalent to the standard C free call -- with the additional semantics as described above. function Realloc (Ptr : System.Address; Size : size_t) return System.Address; -- This is the low level reallocation routine. It takes an existing -- block address returned by a previous call to Alloc or Realloc, -- and reallocates the block. The size can either be increased or -- decreased. If possible the reallocation is done in place, so that -- the returned result is the same as the value of Ptr on entry. -- However, it may be necessary to relocate the block to another -- address, in which case the information is copied to the new -- block, and the old block is freed. The implementation of this -- routine is guaranteed to be task safe, and also aborts are -- deferred as necessary. -- -- If size_t is set to size_t'Last on entry, then a Storage_Error -- exception is raised with a message "object too large". -- -- If size_t is set to zero on entry, then a minimal (but non-zero) -- size block is allocated. -- -- Note: this is roughly equivalent to the standard C realloc call -- with the additional semantics as described above. ------------- -- Monitor -- ------------- procedure Configure (Activate_Monitor : Boolean := False; Disable_Free : Boolean := False; Stack_Trace_Depth : Natural := 30; Maximum_Logically_Freed_Memory : Long_Long_Integer := 50_000_000; Minimum_To_Free : Long_Long_Integer := 0; Reset_Content_On_Free : Boolean := True; Raise_Exceptions : Boolean := False; Advanced_Scanning : Boolean := False; Errors_To_Stdout : Boolean := True; Low_Level_Traces : Boolean := False); -- Configure this package (these are global settings, not task-specific). -- -- If Activate_Monitor is true, GPS will monitor all memory allocations and -- deallocations, and through the Dump procedure below be able to report -- the memory usage. The overhead is almost null when the monitor is -- disabled. -- -- If Disable_Free is true, no deallocation is ever performed. This can be -- temporarily useful when investigating memory issues. -- -- Stack_Trace_Depth. This parameter controls the maximum depth of stack -- traces that are output to indicate locations of actions for error -- conditions such as bad allocations. If set to zero, the debug pool -- will not try to compute backtraces. This is more efficient but gives -- less information on problem locations -- -- Maximum_Logically_Freed_Memory: maximum amount of memory (bytes) -- that should be kept before starting to physically deallocate some. -- This value should be non-zero, since having memory that is logically -- but not physically freed helps to detect invalid memory accesses. -- -- Minimum_To_Free is the minimum amount of memory that should be freed -- every time the pool starts physically releasing memory. The algorithm -- to compute which block should be physically released needs some -- expensive initialization (see Advanced_Scanning below), and this -- parameter can be used to limit the performance impact by ensuring -- that a reasonable amount of memory is freed each time. Even in the -- advanced scanning mode, marked blocks may be released to match this -- Minimum_To_Free parameter. -- -- Reset_Content_On_Free: If true, then the contents of the freed memory -- is reset to the pattern 16#DEADBEEF#, following an old IBM convention. -- This helps in detecting invalid memory references from the debugger. -- -- Raise_Exceptions: If true, the exceptions below will be raised every -- time an error is detected. If you set this to False, then the action -- is to generate output on standard error or standard output, depending -- on Errors_To_Stdout, noting the errors, but to -- keep running if possible (of course if storage is badly damaged, this -- attempt may fail. This helps to detect more than one error in a run. -- -- Advanced_Scanning: If true, the pool will check the contents of all -- allocated blocks before physically releasing memory. Any possible -- reference to a logically free block will prevent its deallocation. -- Note that this algorithm is approximate, and it is recommended -- that you set Minimum_To_Free to a non-zero value to save time. -- -- Errors_To_Stdout: Errors messages will be displayed on stdout if -- this parameter is True, or to stderr otherwise. -- -- Low_Level_Traces: Traces all allocation and deallocations on the -- stream specified by Errors_To_Stdout. This can be used for -- post-processing by your own application, or to debug the -- debug_pool itself. The output indicates the size of the allocated -- block both as requested by the application and as physically -- allocated to fit the additional information needed by the debug -- pool. type Report_Type is new GNAT.Debug_Pools.Report_Type; generic with procedure Put_Line (S : String) is <>; with procedure Put (S : String) is <>; procedure Redirectable_Dump (Size : Positive; Report : Report_Type := All_Reports); -- Dump information about memory usage to configurable output -- Size is the number of the biggest memory users we want to show. Report -- indicates which sorting order is used in the report procedure Dump (Size : Positive; Report : Report_Type := All_Reports); -- Dump information about memory usage. -- Size is the number of the biggest memory users we want to show. Report -- indicates which sorting order is used in the report procedure Reset; -- Reset all internal data. This is in general not needed, unless you want -- to know what memory is used by specific parts of your application procedure Mark_Traceback; -- Add a special chunk in the monitor for the current traceback. This is -- a convenient way to check how many times we go through a given path, -- and where this is called from. -- Nothing is done if the memory monitor has not been activated type Byte_Count is new GNAT.Debug_Pools.Byte_Count; type Watermark_Info is record High : Byte_Count; Current : Byte_Count; end record; function Get_Ada_Allocations return Watermark_Info; -- Return information about the allocations done from Ada. -- This does not include allocations done from other languages. function Get_Allocations return Watermark_Info; -- Return information about the allocations done in any language. -- This uses system calls to find out the program's resident size (RSS) -- information, both the peak and the current size. private pragma Convention (C, Alloc); pragma Convention (C, Free); pragma Convention (C, Realloc); end GNATCOLL.Memory; gnatcoll-core-21.0.0/src/gnatcoll-email.adb0000644000175000017500000020723713661715457020346 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ pragma Ada_2012; with Ada.Calendar; use Ada.Calendar; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Containers; use Ada.Containers; with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Text_IO; use Ada.Text_IO; with Ada.Unchecked_Deallocation; with GNATCOLL.Email.Utils; use GNATCOLL.Email.Utils; with GNATCOLL.Utils; use GNATCOLL.Utils; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNAT.Strings; use GNAT.Strings; package body GNATCOLL.Email is use Header_List, Charset_String_List, Message_List; function Identify_Header (Name : String) return Any_Header; -- Determine whether Name is the name of an Addr_Header or Other_Header procedure To_String (Payload : Message_Payload; Header_Max_Line_Len : Positive; Content_Filter : Payload_Filter := null; Msg : Message'Class; Append_To : in out Unbounded_String); -- Encode the payload in a form suitable to send the message. -- If necessary, this creates the "boundary" for the message. procedure To_String (Headers : Header_List.List; Header_Max_Line_Len : Positive; Subject_Max_Line_Len : Positive; Filter : Header_Filter := null; Append_To : in out Unbounded_String); -- Encode the headers in a form suitable to send the message procedure Get_Param_Index (H : Header'Class; Param_Name : String; C : out Charset_String_List.Cursor; Semicolon : out Integer; Name_Start : out Integer; Name_End : out Integer; Value_End : out Integer); -- Find the occurrence of a parameter in the value of H procedure Replace_Header_Internal (Msg : Message'Class; H : Header'Class; Append : Boolean); -- Same as Replace_Header, but Append can be used to specify whether the -- header should be appended or prepended to the list if it didn't exist -- yet. function Check_Boundary (Msg : Message'Class; Boundary : String) return Boolean; -- Whether Boundary can be used for this message function Has_Line_Starting_With (Text : Unbounded_String; Starts_With : String) return Boolean; -- Whether Text has a line that starts with Starts_With function Clone_Header (Ref : Header) return Header; -- Return a deep copy of the given Header. function Clone_Headers (Ref : Header_List.List) return Header_List.List; -- Return a deep copy of the given list of headers. type Constant_String_Access is access constant String; Encoding_Names : constant array (Encoding_Type) of Constant_String_Access := (Encoding_7bit => new String'("7bit"), Encoding_8bit => new String'("8bit"), Encoding_Binary => new String'("binary"), Encoding_QP => new String'("quoted-printable"), Encoding_Base64 => new String'("base64")); --------- -- "=" -- --------- overriding function "=" (Addr1, Addr2 : Email_Address) return Boolean is begin return To_Lower (To_String (Addr1.Address)) = To_Lower (To_String (Addr2.Address)); end "="; --------------------- -- Next_Occurrence -- --------------------- function Next_Occurrence (S : String; Char : Character; Skip_Quotes : Boolean := False) return Integer is In_Quotes : Boolean := False; begin for Index in S'Range loop if Skip_Quotes and then S (Index) = '"' then In_Quotes := not In_Quotes; elsif S (Index) = Char and then not In_Quotes then return Index; end if; end loop; return S'Last + 1; end Next_Occurrence; --------------------- -- Identify_Header -- --------------------- function Identify_Header (Name : String) return Any_Header is L_Name : constant String := To_Lower (Name); begin if L_Name = "from" or else L_Name = "sender" or else L_Name = "to" or else L_Name = "cc" or else L_Name = "bcc" then return Addr_Header; else return Other_Header; end if; end Identify_Header; ------------------- -- Is_Whitespace -- ------------------- function Is_Whitespace (Char : Character) return Boolean is begin return Char = ' ' or Char = ASCII.HT; end Is_Whitespace; ---------------------- -- Skip_Whitespaces -- ---------------------- procedure Skip_Whitespaces (S : String; Index : in out Integer) is begin while Index <= S'Last and then (Is_Whitespace (S (Index)) or else S (Index) = ASCII.LF) loop Index := Index + 1; end loop; end Skip_Whitespaces; ----------------- -- New_Message -- ----------------- function New_Message (MIME_Type : String := Text_Plain; Charset : String := Charset_US_ASCII) return Message is Pay : Message_Payload; Msg : Message; begin if Get_Main_Type (MIME_Type) = "multipart" then Pay := Null_Multipart_Payload; else Pay := Null_Payload; end if; Msg := (Ada.Finalization.Controlled with Contents => new Message_Record' (Ref_Count => 1, Envelope_From => Null_Unbounded_String, Headers => Header_List.Empty_List, Is_Nested => False, Payload => Pay)); if MIME_Type /= "" then Replace_Header (Msg, Create ("Content-Type", MIME_Type & "; charset=""" & Charset & '"')); end if; return Msg; end New_Message; ------------------ -- Clone_Header -- ------------------ function Clone_Header (Ref : Header) return Header is Copy : constant Header := (Ada.Finalization.Controlled with Contents => new Header_Record); begin Copy.Contents.all := (Name => Ref.Contents.Name, Value => Ref.Contents.Value, Ref_Count => 1); return Copy; end Clone_Header; ------------------- -- Clone_Headers -- ------------------- function Clone_Headers (Ref : Header_List.List) return Header_List.List is Copy : Header_List.List; Cursor : Header_List.Cursor := First (Ref); begin while Cursor /= Header_List.No_Element loop Append (Copy, Clone_Header (Element (Cursor))); Next (Cursor); end loop; return Copy; end Clone_Headers; ------------------- -- Clone_Message -- ------------------- function Clone_Message (Msg : Message) return Message is New_Msg : Message; begin New_Msg := (Ada.Finalization.Controlled with Contents => new Message_Record); New_Msg.Contents.all := (Ref_Count => 1, Envelope_From => Msg.Contents.Envelope_From, Headers => Clone_Headers (Msg.Contents.Headers), Payload => Msg.Contents.Payload, Is_Nested => Msg.Contents.Is_Nested); return New_Msg; end Clone_Message; -------------- -- Reply_To -- -------------- function Reply_To (Msg : Message'Class; From_Email : String; From_Real_Name : String := ""; Quote : Boolean := True; Reply_All : Boolean := True; Reply_Filter : access function (Recipient : Email_Address) return Boolean := null; Local_Date : Ada.Calendar.Time := Ada.Calendar.Clock; Charset : String := Charset_US_ASCII) return Message is Reply : Message := New_Message; H, H2, H3 : Header; Is_First : Boolean; To_Quote : Unbounded_String; Part_Iter : Payload_Iterator; Payload : Message; Who_Quoted : Unbounded_String; begin Set_Envelope_From (Reply, From_Email, Local_Date); H := Get_Header (Msg, "Subject"); if H /= Null_Header then H2 := Create ("Subject", "Re: "); Append (H2, Get_Value (H)); Replace_Header_Internal (Reply, H2, Append => False); end if; Replace_Header_Internal (Reply, Create ("Date", Format_Date (Local_Date)), Append => False); Set_From_Header (Reply, From_Email, From_Real_Name, Charset); H := Get_Header (Msg, "From"); H2 := Create ("To", ""); if H /= Null_Header then Append (H2, Get_Value (H)); Flatten (H.Contents.Value, Result => Who_Quoted); Who_Quoted := Parse_Address (To_String (Who_Quoted)).Address; end if; Add_Header (Reply, H2); if Reply_All then H2 := Create ("Cc", ""); Is_First := True; for Recipient of Get_Recipients (Msg) loop if Reply_Filter = null or else Reply_Filter (Recipient) then if Is_First then Is_First := False; else Append (H2, ", "); end if; Append (H2, Format_Address (Recipient)); end if; end loop; if not Is_First then Add_Header (Reply, H2); end if; end if; H := Get_Header (Msg, "Message-Id"); if H /= Null_Header then H2 := Create ("In-Reply-To", ""); Append (H2, Get_Value (H)); Add_Header (Reply, H2); H2 := Create ("References", ""); H3 := Get_Header (Msg, "References"); if H3 /= Null_Header then Append (H2, Get_Value (H3)); else H3 := Get_Header (Msg, "In-Reply-To"); if H3 /= Null_Header then Append (H2, Get_Value (H3)); end if; end if; Append (H2, Get_Value (H)); Add_Header (Reply, H2); end if; if Quote then if Is_Multipart (Msg) then To_Quote := Null_Unbounded_String; Part_Iter := Get_Payload (Msg); loop Next (Part_Iter, Item => Payload); exit when Payload = Null_Message; if Get_Main_Type (Get_Content_Type (Payload)) = "text" then Get_Single_Part_Payload (Payload, To_Quote, Decode => True); exit; end if; end loop; elsif Get_Main_Type (Get_Content_Type (Msg)) = "text" then Get_Single_Part_Payload (Msg, To_Quote, Decode => True); else To_Quote := Null_Unbounded_String; end if; if To_Quote /= Null_Unbounded_String then if Who_Quoted /= Null_Unbounded_String then Append (Who_Quoted, " wrote:" & ASCII.LF); end if; declare StrA : constant String := To_String (To_Quote); Start, Eol : Integer; begin Start := StrA'First; while Start <= StrA'Last loop Eol := Integer'Min (StrA'Last, Next_Occurrence (StrA (Start .. StrA'Last), ASCII.LF)); Append (Who_Quoted, "> " & StrA (Start .. Eol)); Start := Eol + 1; end loop; Set_Text_Payload (Reply, Who_Quoted); end; end if; end if; return Reply; end Reply_To; ------------------------- -- Set_Default_Headers -- ------------------------- procedure Set_Default_Headers (Msg : in out Message'Class; From_Email : String; Subject : String := "No Subject"; From_Real_Name : String := ""; Local_Date : Ada.Calendar.Time := Ada.Calendar.Clock; Charset : String := Charset_US_ASCII) is begin Set_Envelope_From (Msg, From_Email, Local_Date); Replace_Header_Internal (Msg, Create ("Subject", Subject, Charset), Append => False); Replace_Header_Internal (Msg, Create ("Date", Format_Date (Local_Date)), Append => False); Set_From_Header (Msg, From_Email, From_Real_Name, Charset); end Set_Default_Headers; --------------------- -- Set_From_Header -- --------------------- procedure Set_From_Header (Msg : in out Message'Class; From_Email : String; From_Real_Name : String; Charset : String) is From_H : Header; begin From_H := Create ("From", Charset_String_List.List'(Format_Address (Email => (Real_Name => To_Unbounded_String (From_Real_Name), Address => To_Unbounded_String (From_Email)), Charset => Charset))); Replace_Header_Internal (Msg, From_H, Append => False); end Set_From_Header; ------------ -- Adjust -- ------------ overriding procedure Adjust (Msg : in out Message) is begin if Msg.Contents /= null then Msg.Contents.Ref_Count := Msg.Contents.Ref_Count + 1; end if; end Adjust; -------------- -- Finalize -- -------------- overriding procedure Finalize (Msg : in out Message) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Message_Record, Message_Access); Contents : Message_Access := Msg.Contents; begin Msg.Contents := null; -- Make Finalize idempotent if Contents /= null then Contents.Ref_Count := Contents.Ref_Count - 1; if Contents.Ref_Count = 0 then Unchecked_Free (Contents); end if; end if; end Finalize; ----------------------- -- Set_Envelope_From -- ----------------------- procedure Set_Envelope_From (Msg : in out Message'Class; From : String) is begin Msg.Contents.Envelope_From := To_Unbounded_String (From); end Set_Envelope_From; ----------------------- -- Set_Envelope_From -- ----------------------- procedure Set_Envelope_From (Msg : in out Message'Class; Email : String; Local_Date : Ada.Calendar.Time) is begin Msg.Contents.Envelope_From := To_Unbounded_String ("From " & Email & " " & Format_Date (Local_Date, From_Line => True)); end Set_Envelope_From; ----------------------- -- Get_Envelope_From -- ----------------------- function Get_Envelope_From (Msg : Message'Class) return String is begin return To_String (Msg.Contents.Envelope_From); end Get_Envelope_From; ------------------------ -- Date_From_Envelope -- ------------------------ function Date_From_Envelope (Msg : Message'Class) return Ada.Calendar.Time is Str : constant String := To_String (Msg.Contents.Envelope_From); Index : Natural := Str'First; begin if Str = "" then return No_Time; end if; Index := Index + 5; -- Skips "From " Skip_Whitespaces (Str, Index); while Index <= Str'Last and then not Is_Whitespace (Str (Index)) loop Index := Index + 1; end loop; return To_Time (Str (Index .. Str'Last), Format => Time_Envelope); end Date_From_Envelope; -------------------------- -- Sender_From_Envelope -- -------------------------- function Sender_From_Envelope (Msg : Message'Class) return String is Str : constant String := To_String (Msg.Contents.Envelope_From); Index : Natural := Str'First + 5; -- Skips "From" Stop : Natural; begin Skip_Whitespaces (Str, Index); Stop := Index; while Stop <= Str'Last and then not Is_Whitespace (Str (Stop)) loop Stop := Stop + 1; end loop; return Str (Index .. Stop - 1); end Sender_From_Envelope; ------------ -- Create -- ------------ function Create (Name : String; Value : String; Charset : String := Charset_US_ASCII) return Header is V : Charset_String_List.List; begin Decode_Header (Value, Default_Charset => Charset, Result => V, Where => Identify_Header (Name)); return Create (Name, V); end Create; function Create (Name : String; Value : Charset_String_List.List) return Header is begin return (Ada.Finalization.Controlled with Contents => new Header_Record' (Name => To_Unbounded_String (To_Lower (Name)), Value => Value, others => <>)); end Create; ------------ -- Append -- ------------ procedure Append (H : in out Header'Class; Value : String; Charset : String := Charset_US_ASCII) is L : Charset_String_List.List; begin Decode_Header (Value, Default_Charset => Charset, Result => L, Where => Identify_Header (To_String (H.Contents.Name))); Splice (H.Contents.Value, Charset_String_List.No_Element, L); end Append; ------------ -- Append -- ------------ procedure Append (H : in out Header'Class; Value : Charset_String_List.List) is C : Charset_String_List.Cursor := First (Value); begin while Has_Element (C) loop Append (H.Contents.Value, Element (C)); Next (C); end loop; end Append; --------------- -- To_String -- --------------- procedure To_String (H : Header'Class; Max_Line_Len : Positive := Default_Max_Header_Line_Length; Show_Header_Name : Boolean := True; Result : out Unbounded_String) is begin if H.Contents = null then Result := Null_Unbounded_String; return; end if; declare Max : Positive := Max_Line_Len - 2 - Length (H.Contents.Name); N : String := To_String (H.Contents.Name); Value : constant Charset_String_List.List := Get_Value (H); Encoded : Unbounded_String; Uppercase_Next : Boolean; Next : Natural; begin To_String (Value, Encoded, Identify_Header (N)); if Show_Header_Name then -- Fix up casing of header name if N = "message-id" then N := Message_ID; elsif N = "cc" then N := CC; else if N'Length >= 5 and then N (N'First .. N'First + 4) = "mime-" then N (N'First .. N'First + 3) := "MIME"; Next := N'First + 5; else Next := N'First; end if; Uppercase_Next := True; while Next <= N'Last loop if Uppercase_Next then N (Next) := To_Upper (N (Next)); Uppercase_Next := False; end if; if N (Next) = '-' then Uppercase_Next := True; end if; Next := Next + 1; end loop; end if; end if; -- Fold continuation lines declare Str : String := To_String (Encoded); Last : Natural; Index, Index2 : Integer; Offset : Integer := 0; -- Count of LF characters skipped so far begin -- Flatten the header on a single line, eliminating newline -- characters. for J in Str'Range loop if Str (J) = ASCII.LF then Offset := Offset + 1; elsif Offset > 0 then Str (J - Offset) := Str (J); end if; end loop; Last := Str'Last - Offset; if Show_Header_Name and then Last <= Max then if Last = 0 then -- Empty header Result := To_Unbounded_String (N & ": "); elsif Element (Encoded, 1) = ' ' then Result := To_Unbounded_String (N & ':' & Str (1 .. Last)); else Result := To_Unbounded_String (N & ": " & Str (1 .. Last)); end if; return; elsif not Show_Header_Name and then Last <= Max_Line_Len then if Offset = 0 then Result := Encoded; -- Save a string copy else Result := To_Unbounded_String (Str (1 .. Last)); end if; return; end if; Result := Null_Unbounded_String; Index := Str'First; while Index <= Last loop -- Only split on spaces. To keep Content-Type headers as much -- as possible on a single line, we split on the first blank -- space after the theoretical split point. Index2 := Integer'Min (Index + Max - 1, Last); loop Index2 := Index2 + 1; exit when Index2 > Last or else Str (Index2) = ' '; end loop; -- Index2 points right after last non-blank character Append (Result, Str (Index .. Index2 - 1)); -- Do not print a last line containing only white spaces, this -- might confuse mailers. if Index2 < Last then Append (Result, ASCII.LF & ' '); end if; Index := Index2 + 1; Max := Max_Line_Len; end loop; if Show_Header_Name then if Length (Result) = 0 or else Element (Result, 1) = ' ' then Result := N & ':' & Result; else Result := N & ": " & Result; end if; end if; end; end; end To_String; function To_String (H : Header'Class; Max_Line_Len : Positive := Default_Max_Header_Line_Length; Show_Header_Name : Boolean := True) return String is Result : Unbounded_String; begin To_String (H, Max_Line_Len, Show_Header_Name, Result); return To_String (Result); end To_String; --------------- -- To_String -- --------------- procedure To_String (Payload : Message_Payload; Header_Max_Line_Len : Positive; Content_Filter : Payload_Filter := null; Msg : Message'Class; Append_To : in out Unbounded_String) is C : Message_List.Cursor; Attachment : Message; begin case Payload.Multipart is when True => declare Parts : array (1 .. Length (Payload.Parts)) of Boolean := (others => True); Payload_Count : Natural := Parts'Length; begin -- First check how many payloads needs to be output if Content_Filter /= null then C := First (Payload.Parts); Payload_Count := 0; for P in Parts'Range loop Parts (P) := Content_Filter (Element (C)); if Parts (P) then Payload_Count := Payload_Count + 1; end if; Next (C); end loop; end if; -- At least one payload : create a boundary if necessary if Payload_Count > 0 then Set_Boundary (Msg); end if; declare Boundary : constant String := Get_Boundary (Msg); begin if Payload.Preamble /= Null_Unbounded_String then Append (Append_To, Payload.Preamble); end if; C := First (Payload.Parts); for P in Parts'Range loop if Parts (P) then Append (Append_To, ASCII.LF & "--" & Boundary & ASCII.LF); Attachment := Element (C); To_String (Attachment.Contents.Headers, Header_Max_Line_Len, Header_Max_Line_Len, Append_To => Append_To); To_String (Attachment.Contents.Payload, Header_Max_Line_Len, Msg => Attachment, Append_To => Append_To); end if; Next (C); end loop; Append (Append_To, ASCII.LF & "--" & Boundary & "--" & ASCII.LF); if Payload.Epilogue /= Null_Unbounded_String then Append (Append_To, ASCII.LF & Payload.Epilogue); end if; end; end; when False => Append (Append_To, Payload.Text); end case; end To_String; --------------- -- To_String -- --------------- procedure To_String (Headers : Header_List.List; Header_Max_Line_Len : Positive; Subject_Max_Line_Len : Positive; Filter : Header_Filter := null; Append_To : in out Unbounded_String) is H : Header_List.Cursor := First (Headers); Tmp : Unbounded_String; begin while Has_Element (H) loop if Filter = null or else Filter (Element (H)) then if Get_Name (Element (H)) = "subject" then To_String (Element (H), Subject_Max_Line_Len, Result => Tmp); else To_String (Element (H), Header_Max_Line_Len, Result => Tmp); end if; if Tmp /= Null_Unbounded_String then Append (Append_To, Tmp); Append (Append_To, ASCII.LF); end if; end if; Next (H); end loop; Append (Append_To, ASCII.LF); end To_String; ---------- -- Size -- ---------- function Size (Msg : Message; Include_Attachments : Boolean) return Long_Integer is Total : Long_Integer := 0; C : Message_List.Cursor; begin if Is_Multipart (Msg) then Total := Total + Long_Integer (Length (Msg.Contents.Payload.Preamble)) + Long_Integer (Length (Msg.Contents.Payload.Epilogue)); C := First (Msg.Contents.Payload.Parts); while Has_Element (C) loop if Include_Attachments then Total := Total + Size (Element (C), True); elsif Get_Content_Type (Element (C)) = Text_Plain then Total := Total + Size (Element (C), True); exit; end if; Next (C); end loop; else Total := Total + Long_Integer (Length (Msg.Contents.Payload.Text)); end if; return Total; end Size; --------------- -- To_String -- --------------- procedure To_String (Msg : Message'Class; Envelope : Boolean := False; Header_Max_Line_Len : Positive := Default_Max_Header_Line_Length; Subject_Max_Line_Len : Positive := Default_Max_Header_Line_Length; Content_Filter : Payload_Filter := null; Filter : Header_Filter := null; Decode : Boolean := False; Quote_From : Boolean := False; Result : out Unbounded_String) is Encoded_Payload : Unbounded_String; Payload : Unbounded_String; Encoding : Encoding_Type; Encoding_Str : Unbounded_String; function Retain_Header (H : Header'Class) return Boolean; -- Filter for header list: if Decode is True, strip the MIME -- Content-Transfer-Encoding; else just apply Filter. ------------------- -- Retain_Header -- ------------------- function Retain_Header (H : Header'Class) return Boolean is begin if Decode and then Get_Name (H) = To_Lower (Content_Transfer_Encoding) then return False; elsif Filter /= null then return Filter (H); else return True; end if; end Retain_Header; -- Start of processing for To_String begin Result := Null_Unbounded_String; if Envelope then Append (Result, Msg.Contents.Envelope_From); Append (Result, ASCII.LF); end if; -- First convert the payload. This way we know how many payloads are -- output, and whether a boundary is necessary or not. To_String (Msg.Contents.Payload, Header_Max_Line_Len, Msg => Msg, Content_Filter => Content_Filter, Append_To => Encoded_Payload); if Decode then Encoding := Get_Encoding_Type (Msg); case Encoding is when Encoding_Base64 => Base64_Decode (To_String (Encoded_Payload), Payload); Encoding := Encoding_8bit; when Encoding_QP => Quoted_Printable_Decode (To_String (Encoded_Payload), Payload); Encoding := Encoding_8bit; when others => Payload := Encoded_Payload; end case; else Payload := Encoded_Payload; end if; To_String (Msg.Contents.Headers, Header_Max_Line_Len, Subject_Max_Line_Len, Retain_Header'Unrestricted_Access, Append_To => Result); if Decode and then Encoding /= Encoding_7bit then To_String (Create (Content_Transfer_Encoding, Encoding_Names (Encoding).all), Result => Encoding_Str); -- Splice new CTE header before empty line at end of headers declare L : constant Natural := Length (Result); begin Replace_Slice (Result, L, L - 1, To_String (Encoding_Str) & ASCII.LF); end; end if; if Quote_From then declare Payload_Str : constant String := To_String (Payload); J : Integer := Payload_Str'First; Copy_From : Natural := Payload_Str'First; begin while J < Payload_Str'Last loop -- Skip until the beginning of a new line while J < Payload_Str'Last and then Payload_Str (J) = ASCII.LF loop J := J + 1; end loop; -- If the new line starts with From_ if J + 4 <= Payload_Str'Length and then Payload_Str (J .. J + 4) = "From " then Append (Result, Payload_Str (Copy_From .. J - 1)); Append (Result, ">"); Copy_From := J; J := J + 5; end if; -- Skip till end of line while J < Payload_Str'Last and then Payload_Str (J) /= ASCII.LF loop J := J + 1; end loop; end loop; Append (Result, Payload_Str (Copy_From .. Payload_Str'Last)); end; else Append (Result, Payload); end if; end To_String; ------------- -- To_Time -- ------------- function To_Time (H : Header'Class) return Ada.Calendar.Time is Tmp : Unbounded_String; begin -- For portability, we could use To_String (H.Value), but that is -- slower. if H.Contents = null then return No_Time; else Flatten (H.Contents.Value, Result => Tmp); return To_Time (To_String (Tmp)); end if; end To_Time; ---------------- -- Add_Header -- ---------------- procedure Add_Header (Msg : in out Message'Class; H : Header'Class) is begin Append (Msg.Contents.Headers, Header (H)); end Add_Header; ---------------- -- Get_Header -- ---------------- function Get_Header (Msg : Message'Class; Name : String) return Header is Iter : Header_List.Cursor; N : constant String := To_Lower (Name); begin if Msg.Contents /= null then Iter := First (Msg.Contents.Headers); while Has_Element (Iter) loop if Element (Iter).Contents.Name = N then return Element (Iter); end if; Next (Iter); end loop; end if; return Null_Header; end Get_Header; -------------------- -- Delete_Headers -- -------------------- procedure Delete_Headers (Msg : Message'Class; Name : String) is Iter : Header_List.Cursor := First (Msg.Contents.Headers); Iter2 : Header_List.Cursor; N : constant String := To_Lower (Name); begin while Has_Element (Iter) loop Iter2 := Next (Iter); if Name = "" or else Element (Iter).Contents.Name = N then Delete (Msg.Contents.Headers, Iter); end if; Iter := Iter2; end loop; end Delete_Headers; ------------------- -- Delete_Header -- ------------------- procedure Delete_Header (Msg : Message'Class; H : Header'Class) is Iter : Header_List.Cursor := First (Msg.Contents.Headers); begin while Has_Element (Iter) loop if Element (Iter).Contents = H.Contents then Delete (Msg.Contents.Headers, Iter); return; end if; Next (Iter); end loop; end Delete_Header; -------------------- -- Replace_Header -- -------------------- procedure Replace_Header (Msg : Message'Class; H : Header'Class) is begin Replace_Header_Internal (Msg, H, Append => True); end Replace_Header; ----------------------------- -- Replace_Header_Internal -- ----------------------------- procedure Replace_Header_Internal (Msg : Message'Class; H : Header'Class; Append : Boolean) is Iter : Header_List.Cursor := First (Msg.Contents.Headers); Iter2 : Header_List.Cursor; Is_First : Boolean := True; begin while Has_Element (Iter) loop Iter2 := Next (Iter); if Element (Iter).Contents.Name = H.Contents.Name then if Is_First then Replace_Element (Msg.Contents.Headers, Iter, Header (H)); Is_First := False; else Delete (Msg.Contents.Headers, Iter); end if; end if; Iter := Iter2; end loop; if Is_First then if Append then Header_List.Append (Msg.Contents.Headers, Header (H)); else Prepend (Msg.Contents.Headers, Header (H)); end if; end if; end Replace_Header_Internal; ----------------- -- Get_Headers -- ----------------- function Get_Headers (Msg : Message'Class; Name : String := "") return Header_Iterator is C : Header_List.Cursor; N : constant Unbounded_String := To_Unbounded_String (To_Lower (Name)); begin if Msg.Contents = null then return (Header_List.No_Element, N); end if; C := First (Msg.Contents.Headers); if Name /= "" then while Has_Element (C) and then Element (C).Contents.Name /= N loop Next (C); end loop; end if; return (C, N); end Get_Headers; ---------- -- Next -- ---------- function Next (Iter : in out Header_Iterator; H : out Header) return Boolean is begin if Has_Element (Iter.Cursor) then H := Element (Iter.Cursor); Next (Iter.Cursor); if Length (Iter.Name) /= 0 then while Has_Element (Iter.Cursor) and then Element (Iter.Cursor).Contents.Name /= Iter.Name loop Next (Iter.Cursor); end loop; end if; return True; end if; return False; end Next; ---------- -- Next -- ---------- procedure Next (Iter : in out Header_Iterator; H : out Header) is begin if not Next (Iter, H) then H := Null_Header; end if; end Next; ---------------------- -- Set_Text_Payload -- ---------------------- procedure Set_Text_Payload (Msg : Message'Class; Payload : Unbounded_String; MIME_Type : String := Text_Plain; Disposition : String := ""; Charset : String := Charset_US_ASCII; Prepend : Boolean := False) is Msg2 : Message; H_CT : Header := Create (Content_Type, MIME_Type); H_CTE : Header := Null_Header; begin if Charset /= "" then Set_Param (H_CT, "charset", Charset); H_CTE := Create (Content_Transfer_Encoding, (if Charset = Charset_US_ASCII then "7bit" else "8bit")); end if; if Msg.Contents.Payload.Multipart then Msg2 := New_Message (MIME_Type => ""); Replace_Header (Msg2, H_CT); if H_CTE /= Null_Header then Replace_Header (Msg2, H_CTE); end if; if Disposition /= "" then Add_Header (Msg2, Create (Content_Disposition, Disposition)); end if; Msg2.Contents.Payload.Text := Payload; if Prepend then Message_List.Prepend (Msg.Contents.Payload.Parts, Msg2); else Message_List.Append (Msg.Contents.Payload.Parts, Msg2); end if; else if MIME_Type /= "" and not Prepend then Replace_Header (Msg, H_CT); if H_CTE = Null_Header then Delete_Headers (Msg, Content_Transfer_Encoding); else Replace_Header (Msg, H_CTE); end if; Delete_Headers (Msg, Content_Disposition); end if; if Prepend then Msg.Contents.Payload.Text := Payload & Msg.Contents.Payload.Text; -- Incorrect if Charset does not match the charset of the existing -- payload??? else Msg.Contents.Payload.Text := Payload; end if; end if; end Set_Text_Payload; procedure Set_Text_Payload (Msg : Message'Class; Payload : String; MIME_Type : String := Text_Plain; Disposition : String := ""; Charset : String := Charset_US_ASCII; Prepend : Boolean := False) is begin Set_Text_Payload (Msg => Msg, Payload => To_Unbounded_String (Payload), MIME_Type => MIME_Type, Disposition => Disposition, Charset => Charset, Prepend => Prepend); end Set_Text_Payload; ----------------------------- -- Get_Single_Part_Payload -- ----------------------------- procedure Get_Single_Part_Payload (Msg : Message'Class; Payload : out Unbounded_String; Decode : Boolean := False) is H : Header; Encoding : Encoding_Type; Encoding_Str : Unbounded_String; begin if Msg.Contents.Payload.Multipart then raise Multipart_Error; elsif Decode then H := Get_Header (Msg, Content_Transfer_Encoding); if H.Contents = null then Encoding := Encoding_7bit; else Flatten (H.Contents.Value, Result => Encoding_Str); declare Encode : constant String := To_Lower (Trim (To_String (Encoding_Str), Ada.Strings.Both)); begin if Encode = "base64" then Encoding := Encoding_Base64; elsif Encode = "quoted-printable" then Encoding := Encoding_QP; else Encoding := Encoding_7bit; end if; end; end if; case Encoding is when Encoding_Base64 => Base64_Decode (To_String (Msg.Contents.Payload.Text), Payload); when Encoding_QP => Quoted_Printable_Decode (To_String (Msg.Contents.Payload.Text), Payload); when others => Payload := Msg.Contents.Payload.Text; end case; else Payload := Msg.Contents.Payload.Text; end if; end Get_Single_Part_Payload; -------------- -- Get_Name -- -------------- function Get_Name (H : Header'Class) return String is begin return To_String (H.Contents.Name); end Get_Name; --------------- -- Get_Value -- --------------- function Get_Value (H : Header'Class) return Charset_String_List.List is begin if H.Contents = null then return Charset_String_List.Empty_List; else return H.Contents.Value; end if; end Get_Value; ------------------ -- Set_Epilogue -- ------------------ procedure Set_Epilogue (Msg : in out Message'Class; Epilogue : String) is begin Convert_To_Multipart (Msg); Msg.Contents.Payload.Epilogue := To_Unbounded_String (Epilogue); end Set_Epilogue; ------------------ -- Set_Preamble -- ------------------ procedure Set_Preamble (Msg : in out Message'Class; Preamble : String) is begin Convert_To_Multipart (Msg); Msg.Contents.Payload.Preamble := To_Unbounded_String (Preamble); end Set_Preamble; ------------------ -- Is_Multipart -- ------------------ function Is_Multipart (Msg : Message'Class) return Boolean is begin return Msg.Contents.Payload.Multipart; end Is_Multipart; ----------------- -- Get_Payload -- ----------------- function Get_Payload (Msg : Message'Class) return Payload_Iterator is begin if Msg.Contents.Payload.Multipart then return (Cursor => First (Msg.Contents.Payload.Parts), Msg => Null_Message); else return (Cursor => Message_List.No_Element, Msg => Message (Msg)); end if; end Get_Payload; ---------- -- Next -- ---------- procedure Next (Iter : in out Payload_Iterator; Item : out Message) is begin if Has_Element (Iter.Cursor) then Item := Element (Iter.Cursor); Next (Iter.Cursor); elsif Iter.Msg /= Null_Message then Item := Iter.Msg; Iter.Msg := Null_Message; else Item := Null_Message; end if; end Next; -------------------- -- Delete_Payload -- -------------------- procedure Delete_Payload (Msg : in out Message'Class; Iter : in out Payload_Iterator) is begin Delete (Msg.Contents.Payload.Parts, Iter.Cursor); end Delete_Payload; ---------------------- -- Get_Content_Type -- ---------------------- function Get_Content_Type (Msg : Message'Class) return String is T : constant String := Get_Type (Get_Header (Msg, Content_Type)); begin if T /= "" then return T; elsif Msg.Contents.Is_Nested then return Message_RFC822; else return Text_Plain; end if; end Get_Content_Type; -------------- -- Get_Type -- -------------- function Get_Type (H : Header) return String is use Ada.Strings; H_Ustr : Unbounded_String; begin if H = Null_Header then return ""; end if; Flatten (H.Contents.Value, Result => H_Ustr); declare H_Str : constant String := Trim (To_String (H_Ustr), Both); SC : Integer; begin SC := H_Str'First; while SC <= H_Str'Last and then not (Is_Whitespace (H_Str (SC)) or else H_Str (SC) = ';') loop SC := SC + 1; end loop; return To_Lower (H_Str (H_Str'First .. SC - 1)); end; end Get_Type; ---------------------------- -- Convert_To_Single_Part -- ---------------------------- procedure Convert_To_Single_Part (Msg : in out Message'Class; Purge : Boolean := False) is Attach : Message; begin if Msg.Contents.Payload.Multipart then if Length (Msg.Contents.Payload.Parts) = 0 or else Purge then Msg.Contents.Payload := (Multipart => False, Text => Null_Unbounded_String); Replace_Header (Msg, Create (Content_Type, Text_Plain)); elsif Length (Msg.Contents.Payload.Parts) = 1 then Attach := Element (First (Msg.Contents.Payload.Parts)); if Is_Multipart (Attach) then Msg.Contents.Payload := (Multipart => True, Parts => Attach.Contents.Payload.Parts, Preamble => Attach.Contents.Payload.Preamble, Epilogue => Attach.Contents.Payload.Epilogue); Replace_Header (Msg, Get_Header (Attach, Content_Type)); else Msg.Contents.Payload := (Multipart => False, Text => Attach.Contents.Payload.Text); Replace_Header (Msg, Get_Header (Attach, Content_Type)); if Get_Header (Attach, Content_Transfer_Encoding) /= Null_Header then Replace_Header (Msg, Get_Header (Attach, Content_Transfer_Encoding)); end if; end if; end if; end if; end Convert_To_Single_Part; -------------------------- -- Convert_To_Multipart -- -------------------------- procedure Convert_To_Multipart (Msg : Message'Class) is MIME_Type : constant String := Get_Content_Type (Msg); Is_Multipart_Type : constant Boolean := Get_Main_Type (MIME_Type) = "multipart"; begin if not Msg.Contents.Payload.Multipart then Convert_To_Multipart (Msg, (if Is_Multipart_Type then MIME_Type else Multipart_Mixed)); end if; end Convert_To_Multipart; procedure Convert_To_Multipart (Msg : Message'Class; MIME_Type : String; Force : Boolean := False) is Parts : Message_List.List; Create_Nested : constant Boolean := Get_Content_Type (Msg) /= MIME_Type or else Force; Part : Message; Preamble : Unbounded_String; procedure Move_Header (Header_Name : String); -- Move named header from Msg to Part ----------------- -- Move_Header -- ----------------- procedure Move_Header (Header_Name : String) is H : constant Header := Get_Header (Msg, Header_Name); begin if H /= Null_Header then Replace_Header (Part, H); Delete_Header (Msg, H); end if; end Move_Header; -- Start of processing for Convert_To_Multipart begin if not Msg.Contents.Payload.Multipart or else Create_Nested then if Get_Main_Type (Get_Content_Type (Msg)) = "multipart" and then not Create_Nested then -- Here we only convert the underlying payload storage -- to multipart, but we don't change the user-visible MIME -- structure: from the user's point of view, this is a no-op. -- Used while parsing a message: the payload is initially -- not Multipart, and then converted here lazily when parts -- are parsed. Assume that the original text payload is actually -- the multipart's preamble. Preamble := Msg.Contents.Payload.Text; else -- Here to convert a MIME message with a non-multipart type to a -- multipart, or to force creation of a nested multipart: make -- the original payload a part in the new multipart payload (but -- don't create an empty part). if Msg.Contents.Payload.Multipart or else Length (Msg.Contents.Payload.Text) > 0 then Part := Clone_Message (Message (Msg)); Part.Contents.Headers := Header_List.Empty_List; Move_Header (Content_Type); Move_Header (Content_Transfer_Encoding); Parts.Append (Part); else Delete_Headers (Msg, Content_Transfer_Encoding); end if; Replace_Header (Msg, Create (Content_Type, MIME_Type)); Replace_Header (Msg, Create (MIME_Version, "1.0")); end if; Msg.Contents.Payload := (Multipart => True, Parts => Parts, Preamble => Preamble, Epilogue => Null_Unbounded_String); end if; end Convert_To_Multipart; ----------------- -- Add_Payload -- ----------------- procedure Add_Payload (Msg : in out Message'Class; Payload : Message; First : Boolean := False) is begin Convert_To_Multipart (Msg); Payload.Contents.Is_Nested := True; if First then Prepend (Msg.Contents.Payload.Parts, Payload); else Append (Msg.Contents.Payload.Parts, Payload); end if; end Add_Payload; ---------------- -- Attach_Msg -- ---------------- procedure Attach_Msg (Msg : in out Message'Class; Attach : Message'Class; Description : String := "") is Attachment : constant Message := New_Message (MIME_Type => Message_RFC822); Tmp : Unbounded_String; begin if Description /= "" then Replace_Header (Attachment, Create (Content_Description, Description)); end if; To_String (Attach, Result => Tmp); Set_Text_Payload (Attachment, Tmp, Charset => "", MIME_Type => Message_RFC822); Replace_Header (Attachment, Create (Content_Disposition, "inline")); Add_Payload (Msg, Attachment); end Attach_Msg; ------------ -- Attach -- ------------ procedure Attach (Msg : in out Message'Class; Path : Virtual_File; MIME_Type : String := Application_Octet_Stream; Recommended_Filename : Virtual_File := No_File; Description : String := ""; Charset : String := Charset_US_ASCII; Disposition : Disposition_Type := Disposition_Attachment; Encoding : Encoding_Type := Encoding_Base64) is Attachment : Message := New_Message (MIME_Type => ""); Str : GNAT.Strings.String_Access; begin declare F : Unbounded_String; begin Convert_To_Multipart (Msg); if Get_Main_Type (MIME_Type) = "text" then Replace_Header (Attachment, Create (Content_Type, MIME_Type & "; charset=""" & Charset & '"')); else Replace_Header (Attachment, Create (Content_Type, MIME_Type)); end if; if Description /= "" then Replace_Header (Attachment, Create (Content_Description, Description)); end if; case Disposition is when Disposition_Attachment => if Recommended_Filename = No_File then Replace_Header (Attachment, Create (Content_Disposition, "attachment; filename=""" & (+Base_Name (Path)) & '"')); else Replace_Header (Attachment, Create (Content_Disposition, "attachment; filename=""" & (+Base_Name (Recommended_Filename)) & '"')); end if; when Disposition_Inline => if Recommended_Filename = No_File then Replace_Header (Attachment, Create (Content_Disposition, "inline; filename=""" & (+Base_Name (Path)) & '"')); else Replace_Header (Attachment, Create (Content_Disposition, "inline; filename=""" & (+Base_Name (Recommended_Filename)) & '"')); end if; end case; Str := Read_File (Path); case Encoding is when Encoding_Base64 => Base64_Encode (Str.all, Charset => Charset, Where => Text, Result => F); Add_Header (Attachment, Create (Content_Transfer_Encoding, "base64")); Set_Unbounded_String (Attachment.Contents.Payload.Text, To_String (F)); when Encoding_QP => Quoted_Printable_Encode (Str.all, Charset => Charset, Where => Text, Result => F); Add_Header (Attachment, Create (Content_Transfer_Encoding, "quoted-printable")); Set_Unbounded_String (Attachment.Contents.Payload.Text, To_String (F)); when Encoding_7bit => Add_Header (Attachment, Create (Content_Transfer_Encoding, "7bit")); Set_Unbounded_String (Attachment.Contents.Payload.Text, Str.all); when Encoding_8bit => Add_Header (Attachment, Create (Content_Transfer_Encoding, "8bit")); Set_Unbounded_String (Attachment.Contents.Payload.Text, Str.all); when Encoding_Binary => Add_Header (Attachment, Create (Content_Transfer_Encoding, "binary")); Set_Unbounded_String (Attachment.Contents.Payload.Text, Str.all); end case; end; Free (Str); Append (Msg.Contents.Payload.Parts, Attachment); end Attach; --------------------- -- Get_Param_Index -- --------------------- procedure Get_Param_Index (H : Header'Class; Param_Name : String; C : out Charset_String_List.Cursor; Semicolon : out Integer; Name_Start : out Integer; Name_End : out Integer; Value_End : out Integer) is begin C := First (H.Contents.Value); Semicolon := 0; -- Initialize variables to avoid GNAT warnings Name_Start := 0; Name_End := 0; Value_End := 0; while Has_Element (C) loop declare Str : constant String := To_String (Element (C).Contents); Index : Natural := Str'First; Stop : Natural; Val_Stop : Natural; begin while Index <= Str'Last loop -- Look for next occurrence of ';', but not within a quoted -- string, for instance a filename in Content-Disposition. Index := Next_Occurrence (Str (Index .. Str'Last), ';', Skip_Quotes => True); if Index <= Str'Last then Semicolon := Index; Index := Index + 1; Skip_Whitespaces (Str, Index); Stop := Next_Occurrence (Str (Index + 1 .. Str'Last), '=', Skip_Quotes => True); if Stop < Str'Last then Val_Stop := Next_Occurrence (Str (Stop + 1 .. Str'Last), ';', Skip_Quotes => True); if To_Lower (Str (Index .. Stop - 1)) = To_Lower (Param_Name) then Name_Start := Index; Name_End := Stop - 1; Value_End := Val_Stop - 1; return; end if; Index := Val_Stop; else Index := Stop; end if; end if; end loop; end; Next (C); end loop; end Get_Param_Index; --------------- -- Set_Param -- --------------- procedure Set_Param (H : in out Header'Class; Param_Name : String; Param_Value : String) is C : Charset_String_List.Cursor := First (H.Contents.Value); Semicolon, Name_Start, Name_End, Val_End : Integer; Str : constant String := "; " & Param_Name & "=""" & Param_Value & '"'; begin Get_Param_Index (H, Param_Name, C, Semicolon, Name_Start, Name_End, Val_End); if Has_Element (C) then Replace_Element (H.Contents.Value, C, (Contents => Unbounded_Slice (Element (C).Contents, 1, Semicolon - 1) & Str & Unbounded_Slice (Element (C).Contents, Val_End + 1, Length (Element (C).Contents)), Charset => Element (C).Charset)); else Append (H.Contents.Value, (Contents => To_Unbounded_String (Str), Charset => To_Unbounded_String (Charset_US_ASCII))); end if; end Set_Param; --------------- -- Get_Param -- --------------- function Get_Param (H : Header'Class; Param_Name : String) return String is C : Charset_String_List.Cursor; Semicolon, Name_Start, Name_End, Val_End : Integer; function Get_Val return String; -- Return the value, omitting surrounding quotes if any function Get_Val return String is Str : constant String := Slice (Element (C).Contents, Name_End + 2, Val_End); begin if Str (Str'First) = '"' then return Str (Str'First + 1 .. Str'Last - 1); else return Str; end if; end Get_Val; begin if H.Contents /= null then C := First (H.Contents.Value); Get_Param_Index (H, Param_Name, C, Semicolon, Name_Start, Name_End, Val_End); if Has_Element (C) then return Get_Val; else -- Support for continuation headers -- http://greenbytes.de/tech/webdav/rfc2231.html#rfc.section.3 -- where a header can be split onto several lines declare Current : Natural := 0; Val : Unbounded_String; begin loop Get_Param_Index (H, Param_Name & "*" & Image (Current, Min_Width => 1), C, Semicolon, Name_Start, Name_End, Val_End); exit when not Has_Element (C); Append (Val, Get_Val); Current := Current + 1; end loop; return To_String (Val); end; end if; end if; return ""; end Get_Param; ------------------ -- Delete_Param -- ------------------ procedure Delete_Param (H : in out Header'Class; Param_Name : String) is C : Charset_String_List.Cursor := First (H.Contents.Value); Semicolon, Name_Start, Name_End, Val_End : Integer; begin Get_Param_Index (H, Param_Name, C, Semicolon, Name_Start, Name_End, Val_End); if Has_Element (C) then Replace_Element (H.Contents.Value, C, (Contents => Unbounded_Slice (Element (C).Contents, 1, Semicolon - 1) & Unbounded_Slice (Element (C).Contents, Val_End + 1, Length (Element (C).Contents)), Charset => Element (C).Charset)); end if; end Delete_Param; ------------------ -- Get_Boundary -- ------------------ function Get_Boundary (Msg : Message'Class) return String is Content_T : constant Header := Get_Header (Msg, Content_Type); begin if Content_T = Null_Header then return ""; end if; return Get_Param (Content_T, "boundary"); end Get_Boundary; ----------------------- -- Get_Encoding_Type -- ----------------------- function Get_Encoding_Type (Msg : Message'Class) return Encoding_Type is H : constant Header := Get_Header (Msg, Content_Transfer_Encoding); HU : Unbounded_String; begin if H /= Null_Header then Flatten (H.Contents.Value, Result => HU); declare use Ada.Strings; HS : constant String := To_Lower (Trim (To_String (HU), Both)); begin for J in Encoding_Names'Range loop if HS = Encoding_Names (J).all then return J; end if; end loop; end; end if; return Encoding_7bit; end Get_Encoding_Type; ---------------------------- -- Has_Line_Starting_With -- ---------------------------- function Has_Line_Starting_With (Text : Unbounded_String; Starts_With : String) return Boolean is StrA : constant String := To_String (Text); Index : Natural; Eol : Natural; begin Index := StrA'First; while Index <= StrA'Last loop Eol := Next_Occurrence (StrA (Index .. StrA'Last), ASCII.LF); if Index + Starts_With'Length - 1 <= StrA'Last and then StrA (Index .. Index + Starts_With'Length - 1) = Starts_With then return True; end if; Index := Eol + 1; end loop; return False; end Has_Line_Starting_With; -------------------- -- Check_Boundary -- -------------------- function Check_Boundary (Msg : Message'Class; Boundary : String) return Boolean is Iter : Payload_Iterator; Msg2 : Message; Bound : constant String := "--" & Boundary; begin if Is_Multipart (Msg) then if Has_Line_Starting_With (Msg.Contents.Payload.Preamble, Bound) or else Has_Line_Starting_With (Msg.Contents.Payload.Epilogue, Bound) then return False; end if; Iter := Get_Payload (Msg); loop Next (Iter, Item => Msg2); exit when Msg2 = Null_Message; case Get_Encoding_Type (Msg2) is when Encoding_QP | Encoding_Base64 => -- No check needs to be done, since the boundary always -- includes the =_ sequence which cannot occur in such -- contexts null; when others => if not Check_Boundary (Msg2, Boundary) then return False; end if; end case; end loop; else if Has_Line_Starting_With (Msg.Contents.Payload.Text, Bound) then return False; end if; end if; return True; end Check_Boundary; ------------------ -- Set_Boundary -- ------------------ procedure Set_Boundary (Msg : Message'Class; Boundary : String := "") is Candidate : Unbounded_String; Valid : Boolean := False; Content_T : Header; begin Convert_To_Multipart (Msg); Content_T := Get_Header (Msg, Content_Type); if Boundary = "" then -- Try to reuse the current boundary, if any Candidate := To_Unbounded_String (Get_Boundary (Msg)); if Candidate = "" then -- Else default on an unlikely one -- Should generate a unique string??? Candidate := To_Unbounded_String ("=_=_=____=_=_"); end if; else -- Try and use the user's proposal Candidate := To_Unbounded_String (Boundary); if Index (Candidate, "=_") = 0 then -- Add this string so that we never have to check quoted-printable -- or base64 content Append (Candidate, "=_"); end if; end if; while not Valid loop Valid := Check_Boundary (Msg, To_String (Candidate)); if not Valid then Append (Candidate, "=_"); end if; end loop; if Content_T = Null_Header then Content_T := Create (Content_Type, Multipart_Mixed); end if; Set_Param (Content_T, "boundary", To_String (Candidate)); Replace_Header (Msg, Content_T); end Set_Boundary; ------------ -- Adjust -- ------------ overriding procedure Adjust (H : in out Header) is begin if H.Contents /= null then H.Contents.Ref_Count := H.Contents.Ref_Count + 1; end if; end Adjust; -------------- -- Finalize -- -------------- overriding procedure Finalize (H : in out Header) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Header_Record, Header_Access); begin if H.Contents /= null then H.Contents.Ref_Count := H.Contents.Ref_Count - 1; if H.Contents.Ref_Count = 0 then Unchecked_Free (H.Contents); end if; end if; end Finalize; -------------------- -- Get_Message_Id -- -------------------- function Get_Message_Id (Msg : Message) return String is use Ada.Strings; H : constant Header := Get_Header (Msg, "Message-ID"); MsgId_Str : constant String := (if H = Null_Header then "" else Trim (To_String (H, Show_Header_Name => False), Both)); Index : Integer; begin -- Note that we remove leading and trailing spaces, so that a Message-Id -- that consists only of spaces will be treated as missing. -- Lotus Notes is known to generate such bogus message IDs. Index := Next_Occurrence (MsgId_Str, '<'); if Index > MsgId_Str'Last then return MsgId_Str; else return MsgId_Str (Index + 1 .. Next_Occurrence (MsgId_Str (Index .. MsgId_Str'Last), '>') - 1); end if; end Get_Message_Id; -------------- -- Get_Date -- -------------- function Get_Date (Msg : Message) return Ada.Calendar.Time is H : constant Header := Get_Header (Msg, "Date"); begin if H /= Null_Header then return To_Time (H); else return Date_From_Envelope (Msg); end if; end Get_Date; procedure Debug_Message (Msg : Message); pragma Export (Ada, Debug_Message); -- Display Msg on standard output (for debugging purposes) ------------------- -- Debug_Message -- ------------------- procedure Debug_Message (Msg : Message) is Res : Unbounded_String; begin To_String (Msg, True, Default_Max_Header_Line_Length, Default_Max_Header_Line_Length, null, null, False, True, Res); Put_Line (To_String (Res)); end Debug_Message; end GNATCOLL.Email; gnatcoll-core-21.0.0/src/gnatcoll-geometry.adb0000644000175000017500000004620313661715457021104 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- Algorithms in this package are adapted from the following books and -- articles: -- -- [GGII] Graphic Gems II -- http://www1.acm.org/pubs/tog/GraphicsGems/gemsii -- [GGIV] Graphic Gems IV -- http://www1.acm.org/pubs/tog/GraphicsGems/gemsiv -- [CGA] comp.lang.graphics -- [TRI] http://www.acm.org/jgt/papers/GuigueDevillers03/ -- triangle_triangle_intersection.html -- [PTTRI] http://www.blackpawn.com/texts/pointinpoly/default.html -- [SEGSEG] http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/ -- [GEO] http://geometryalgorithms.com/Archive/algorithm_0.04 -- -- See also the FAQ of comp.graphics.algorithms package body GNATCOLL.Geometry is use Coordinate_Elementary_Functions; function Orient (P, Q, R : Point) return Coordinate; -- From [TRI] pragma Inline (Orient); function Tri_Intersection (P1, Q1, R1, P2, Q2, R2 : Point) return Boolean; -- From [TRI] function Intersection_Test_Edge (P1, Q1, R1, P2, Q2, R2 : Point) return Boolean; -- From [TRI] pragma Inline (Intersection_Test_Edge); function Intersection_Test_Vertex (P1, Q1, R1, P2, Q2, R2 : Point) return Boolean; -- From [TRI] pragma Inline (Intersection_Test_Vertex); -- Return whether the 2.0 triangles intersect. Points need to be sorted ------------- -- To_Line -- ------------- function To_Line (P1, P2 : Point) return Line is A : constant Coordinate := P2.Y - P1.Y; B : constant Coordinate := P1.X - P2.X; begin return (A => A, B => B, C => A * P1.X + B * P1.Y); end To_Line; ------------- -- To_Line -- ------------- function To_Line (Seg : Segment) return Line is begin return To_Line (Seg (1), Seg (2)); end To_Line; -------------- -- Bisector -- -------------- function Bisector (S : Segment) return Line is L : constant Line := To_Line (S); X_Mid : constant Coordinate := (S (1).X + S (2).X) / 2.0; Y_Mid : constant Coordinate := (S (1).Y + S (2).Y) / 2.0; begin return (A => -L.B, B => L.A, C => -L.B * X_Mid + L.A * Y_Mid); end Bisector; ------------------ -- Intersection -- ------------------ function Intersection (L1, L2 : Line) return Point is Det : constant Coordinate := L1.A * L2.B - L2.A * L1.B; begin if Det = 0.0 then if L1.C = L2.C then return Infinity_Points; else return No_Point; end if; else return (X => (L2.B * L1.C - L1.B * L2.C) / Det, Y => (L1.A * L2.C - L2.A * L1.C) / Det); end if; end Intersection; ------------ -- Inside -- ------------ function Inside (P : Point; L : Line) return Boolean is begin return L.A * P.X + L.B * P.Y = L.C; end Inside; ------------ -- Inside -- ------------ function Inside (P : Point; S : Segment) return Boolean is begin return Inside (P, To_Line (S)) and then P.X >= Coordinate'Min (S (1).X, S (2).X) and then P.X <= Coordinate'Max (S (1).X, S (2).X) and then P.Y >= Coordinate'Min (S (1).Y, S (2).Y) and then P.Y <= Coordinate'Max (S (1).Y, S (2).Y); end Inside; ------------------ -- Intersection -- ------------------ -- Algorithm adapted from [GGII - xlines.c] function Intersection (S1, S2 : Segment) return Point is L1 : constant Line := To_Line (S1); R3 : constant Coordinate := L1.A * S2 (1).X + L1.B * S2 (1).Y - L1.C; R4 : constant Coordinate := L1.A * S2 (2).X + L1.B * S2 (2).Y - L1.C; L2 : constant Line := To_Line (S2); R1 : constant Coordinate := L2.A * S1 (1).X + L2.B * S1 (1).Y - L2.C; R2 : constant Coordinate := L2.A * S1 (2).X + L2.B * S1 (2).Y - L2.C; Denom : Coordinate; begin -- Check signs of R3 and R4. If both points 3 and 4 lie on same side -- of line 1, the line segments do not intersect if (R3 > 0.0 and then R4 > 0.0) or else (R3 < 0.0 and then R4 < 0.0) then return No_Point; end if; -- Check signs of r1 and r2. If both points lie on same side of -- second line segment, the line segments do not intersect if (R1 > 0.0 and then R2 > 0.0) or else (R1 < 0.0 and then R2 < 0.0) then return No_Point; end if; -- Line segments intersect, compute intersection point Denom := L1.A * L2.B - L2.A * L1.B; if Denom = 0.0 then -- colinears if Inside (S1 (1), S2) or else Inside (S1 (2), S2) then return Infinity_Points; else return No_Point; end if; end if; return (X => (L2.B * L1.C - L1.B * L2.C) / Denom, Y => (L1.A * L2.C - L2.A * L1.C) / Denom); end Intersection; --------------- -- To_Vector -- --------------- function To_Vector (S : Segment) return Vector is begin return (X => S (2).X - S (1).X, Y => S (2).Y - S (1).Y); end To_Vector; --------- -- "-" -- --------- function "-" (P2, P1 : Point) return Vector is begin return (X => P2.X - P1.X, Y => P2.Y - P1.Y); end "-"; --------- -- Dot -- --------- function Dot (Vector1, Vector2 : Vector) return Coordinate is begin return Vector1.X * Vector2.X + Vector1.Y * Vector2.Y; end Dot; ----------- -- Cross -- ----------- function Cross (Vector1, Vector2 : Vector) return Coordinate is begin return Vector1.X * Vector2.Y - Vector1.Y * Vector2.X; end Cross; ------------ -- Length -- ------------ function Length (Vect : Vector) return Distance_Type is begin return Sqrt (Coordinate (Vect.X * Vect.X + Vect.Y * Vect.Y)); end Length; -------------- -- Distance -- -------------- function Distance (From : Point; To : Line) return Distance_Type is S : constant Coordinate'Base := To.A * To.A + To.B * To.B; begin return abs (To.A * From.X + To.B * From.Y - To.C) / Sqrt (S); end Distance; -------------- -- Distance -- -------------- function Distance (From : Point; To : Point) return Distance_Type is X : constant Coordinate'Base := To.X - From.X; Y : constant Coordinate'Base := To.Y - From.Y; begin return Sqrt (X * X + Y * Y); end Distance; -------------- -- Distance -- -------------- function Distance (From : Point; To : Segment) return Distance_Type is begin if To (1) = To (2) then raise Program_Error with "Empty Segment"; end if; if Dot (From - To (2), To (2) - To (1)) > 0.0 then -- Closest point is Segment (2) return Distance (From, To (2)); elsif Dot (From - To (1), To (1) - To (2)) > 0.0 then -- Closest point is Segment (1) return Distance (From, To (1)); else return Distance (From, To_Line (To)); end if; end Distance; -------------- -- Distance -- -------------- function Distance (From : Point; To : Polygon) return Distance_Type is Min : Distance_Type := Distance_Type'Last; begin for P in To'First .. To'Last - 1 loop Min := Distance_Type'Min (Min, Distance (From, Segment'(To (P), To (P + 1)))); end loop; return Distance_Type'Min (Min, Distance (From, Segment'(To (To'First), To (To'Last)))); end Distance; --------------- -- Intersect -- --------------- function Intersect (C1, C2 : Circle) return Boolean is begin return Distance (C1.Center, C2.Center) <= C1.Radius + C2.Radius; end Intersect; --------------- -- Intersect -- --------------- function Intersect (L : Line; C : Circle) return Boolean is begin return Distance (C.Center, L) <= C.Radius; end Intersect; ---------- -- Area -- ---------- function Area (Self : Polygon) return Distance_Type is D : Coordinate'Base := 0.0; begin for P in Self'First + 1 .. Self'Last - 1 loop D := D + Cross (Self (P) - Self (Self'First), Self (P + 1) - Self (Self'First)); end loop; return abs (D / 2.0); end Area; ---------- -- Area -- ---------- function Area (Self : Triangle) return Distance_Type is begin return abs ((Self (2).X - Self (1).X) * (Self (3).Y - Self (1).Y) - (Self (3).X - Self (1).X) * (Self (2).Y - Self (1).Y)) / 2.0; end Area; --------------- -- To_Circle -- --------------- function To_Circle (P1, P2, P3 : Point) return Circle is -- Find the intersection of the 2.0 perpendicular bisectors of two of -- the segments. Bis1 : constant Line := Bisector (Segment'(1 => P1, 2 => P2)); Bis2 : constant Line := Bisector (Segment'(1 => P2, 2 => P3)); Center : constant Point := Intersection (Bis1, Bis2); begin if Center = No_Point or else Center = Infinity_Points then return No_Circle; else return (Center => Center, Radius => Distance (Center, P1)); end if; end To_Circle; ------------ -- Inside -- ------------ function Inside (P : Point; Poly : Polygon) return Boolean is J : Natural := Poly'Last; C : Boolean := False; Deltay : Coordinate; begin -- See http://www.ecse.rpi.edu/Homepages/wrf/Research -- /Short_Notes/pnpoly.html for S in Poly'Range loop Deltay := P.Y - Poly (S).Y; -- The divide below is mandatory: if you transform it into a -- multiplication on the other side, the sign of the denominator will -- flip the inequality, and thus make the code harder. if ((0.0 <= Deltay and then P.Y < Poly (J).Y) or else (Poly (J).Y <= P.Y and then Deltay < 0.0)) and then (P.X - Poly (S).X < (Poly (J).X - Poly (S).X) * Deltay / (Poly (J).Y - Poly (S).Y)) then C := not C; end if; J := S; end loop; return C; end Inside; -------------- -- Centroid -- -------------- function Centroid (Self : Polygon) return Point is X, Y : Coordinate'Base := 0.0; Weight : Coordinate'Base := 0.0; Local : Coordinate'Base; begin for P in Self'First + 1 .. Self'Last - 1 loop Local := Area (Triangle'(Self (Self'First), Self (P), Self (P + 1))); Weight := Weight + Local; X := X + (Self (Self'First).X + Self (P).X + Self (P + 1).X) / 3.0 * Local; Y := Y + (Self (Self'First).Y + Self (P).Y + Self (P + 1).Y) / 3.0 * Local; end loop; return (X => X / Weight, Y => Y / Weight); end Centroid; --------------- -- Same_Side -- --------------- function Same_Side (P1, P2 : Point; As : Segment) return Boolean is -- Direction of cross-product for (L2 - L1) x (P1 - L1) Cross1_Z : constant Coordinate'Base := (As (2).X - As (1).X) * (P1.Y - As (1).Y) - (As (2).Y - As (1).Y) * (P1.X - As (1).X); -- Direction of cross-product for (L2 - L1) x (P2 - L1) Cross2_Z : constant Coordinate'Base := (As (2).X - As (1).X) * (P2.Y - As (1).Y) - (As (2).Y - As (1).Y) * (P2.X - As (1).X); begin if Cross1_Z <= 0.0 then return Cross2_Z <= 0.0; else return Cross2_Z > 0.0; end if; end Same_Side; --------------- -- Same_Side -- --------------- function Same_Side (P1, P2 : Point; As : Line) return Boolean is S : Segment; begin if As.B = 0.0 then -- Horizontal line S (1).X := As.C / As.A; S (1).Y := Coordinate'First; S (2).X := S (1).X; S (2).Y := Coordinate'Last; else S (1).X := Coordinate'First; S (1).Y := As.C / As.B; S (2).X := Coordinate'Last; S (2).Y := (As.C - As.A * S (2).X) / As.B; end if; return Same_Side (P1, P2, S); end Same_Side; ------------ -- Inside -- ------------ -- Algorithm from [PTTRI] function Inside (P : Point; T : Triangle) return Boolean is begin -- On boundary ? if Distance (P, T (1)) = 0.0 or else Distance (P, T (2)) = 0.0 or else Distance (P, T (3)) = 0.0 then return True; end if; return Same_Side (P, T (3), Segment'(T (1), T (2))) and then Same_Side (P, T (1), Segment'(T (2), T (3))) and then Same_Side (P, T (2), Segment'(T (1), T (3))); end Inside; --------------- -- Orient -- --------------- function Orient (P, Q, R : Point) return Coordinate is begin return (P.X - R.X) * (Q.Y - R.Y) - (P.Y - R.Y) * (Q.X - R.X); end Orient; ------------------------------ -- Intersection_Test_Vertex -- ------------------------------ function Intersection_Test_Vertex (P1, Q1, R1, P2, Q2, R2 : Point) return Boolean is begin if Orient (R2, P2, Q1) >= 0.0 then if Orient (R2, Q2, Q1) <= 0.0 then if Orient (P1, P2, Q1) > 0.0 then return Orient (P1, Q2, Q1) <= 0.0; else if Orient (P1, P2, R1) >= 0.0 then return Orient (Q1, R1, P2) >= 0.0; else return False; end if; end if; else if Orient (P1, Q2, Q1) <= 0.0 then if Orient (R2, Q2, R1) <= 0.0 then return Orient (Q1, R1, Q2) >= 0.0; else return False; end if; else return False; end if; end if; else if Orient (R2, P2, R1) >= 0.0 then if Orient (Q1, R1, R2) >= 0.0 then return Orient (P1, P2, R1) >= 0.0; else if Orient (Q1, R1, Q2) >= 0.0 then return Orient (R2, R1, Q2) >= 0.0; else return False; end if; end if; else return False; end if; end if; end Intersection_Test_Vertex; ---------------------------- -- Intersection_Test_Edge -- ---------------------------- function Intersection_Test_Edge (P1, Q1, R1, P2, Q2, R2 : Point) return Boolean is pragma Unreferenced (Q2); begin if Orient (R2, P2, Q1) >= 0.0 then if Orient (P1, P2, Q1) >= 0.0 then return Orient (P1, Q1, R2) >= 0.0; else return Orient (Q1, R1, P2) >= 0.0 and then Orient (R1, P1, P2) >= 0.0; end if; else if Orient (R2, P2, R1) >= 0.0 then if Orient (P1, P2, R1) >= 0.0 then return Orient (P1, R1, R2) >= 0.0 or else Orient (Q1, R1, R2) >= 0.0; else return False; end if; else return False; end if; end if; end Intersection_Test_Edge; ------------------------- -- Tri_Intersection -- ------------------------- function Tri_Intersection (P1, Q1, R1, P2, Q2, R2 : Point) return Boolean is begin pragma Warnings (Off, "*actuals for this call may be in wrong order"); if Orient (P2, Q2, P1) >= 0.0 then if Orient (Q2, R2, P1) >= 0.0 then if Orient (R2, P2, P1) >= 0.0 then return True; else return Intersection_Test_Edge (P1, Q1, R1, P2, Q2, R2); end if; else if Orient (R2, P2, P1) >= 0.0 then return Intersection_Test_Edge (P1, Q1, R1, R2, P2, Q2); else return Intersection_Test_Vertex (P1, Q1, R1, P2, Q2, R2); end if; end if; else if Orient (Q2, R2, P1) >= 0.0 then if Orient (R2, P2, P1) >= 0.0 then return Intersection_Test_Edge (P1, Q1, R1, Q2, R2, P2); else return Intersection_Test_Vertex (P1, Q1, R1, Q2, R2, P2); end if; else return Intersection_Test_Vertex (P1, Q1, R1, R2, P2, Q2); end if; end if; pragma Warnings (On, "*actuals for this call may be in wrong order"); end Tri_Intersection; --------------- -- Intersect -- --------------- -- From [TRI] function Intersect (T1, T2 : Triangle) return Boolean is begin if Orient (T1 (1), T1 (2), T1 (3)) < 0.0 then if Orient (T2 (1), T2 (2), T2 (3)) < 0.0 then return Tri_Intersection (T1 (1), T1 (3), T1 (2), T2 (1), T2 (3), T2 (2)); else return Tri_Intersection (T1 (1), T1 (3), T1 (2), T2 (1), T2 (2), T2 (3)); end if; else if Orient (T2 (1), T2 (2), T2 (3)) < 0.0 then return Tri_Intersection (T1 (1), T1 (2), T1 (3), T2 (1), T2 (3), T2 (2)); else return Tri_Intersection (T1 (1), T1 (2), T1 (3), T2 (1), T2 (2), T2 (3)); end if; end if; end Intersect; --------------- -- Intersect -- --------------- function Intersect (R1, R2 : Rectangle) return Boolean is begin return not (R1 (1).X > R2 (2).X -- R1 on the right of R2 or else R2 (1).X > R1 (2).X -- R2 on the right of R1 or else R1 (1).Y > R2 (2).Y -- R1 below R2 or else R2 (1).Y > R1 (2).Y); -- R1 above R2 end Intersect; end GNATCOLL.Geometry; gnatcoll-core-21.0.0/src/gnatcoll-utils.adb0000644000175000017500000010102013743647711020374 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Calendar.Formatting; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Command_Line; with Ada.Environment_Variables; with Ada.Strings.Fixed; use Ada.Strings; with Ada.Strings.Maps; use Ada.Strings.Maps; with GNAT.Calendar.Time_IO; with GNAT.Case_Util; with GNAT.OS_Lib; with GNAT.Strings; use GNAT.Strings; with GNATCOLL.String_Builders; with System; package body GNATCOLL.Utils is function Count_For_Split (Str : String; On : Character; Omit_Empty_Lines : Boolean := True) return Natural; -- Returns the number of strings that will occur after splitting Str on On. ---------- -- Free -- ---------- procedure Free (List : in out GNAT.Strings.String_List) is begin for L in List'Range loop Free (List (L)); end loop; end Free; ------------------- -- Is_Whitespace -- ------------------- function Is_Whitespace (Char : Character) return Boolean is begin if Char = ' ' or else Char = ASCII.HT or else Char = ASCII.LF or else Char = ASCII.CR then return True; end if; return False; end Is_Whitespace; ----------- -- Equal -- ----------- function Equal (S1, S2 : String; Case_Sensitive : Boolean) return Boolean is J1 : Natural; J2 : Natural; begin if Case_Sensitive then return S1 = S2; else if S1'Length /= S2'Length then return False; end if; J1 := S1'First; J2 := S2'First; while J1 <= S1'Last loop if To_Lower (S1 (J1)) /= To_Lower (S2 (J2)) then return False; end if; J1 := J1 + 1; J2 := J2 + 1; end loop; return True; end if; end Equal; ---------------------------- -- Case_Insensitive_Equal -- ---------------------------- function Case_Insensitive_Equal (S1, S2 : String) return Boolean is begin return Equal (S1, S2, Case_Sensitive => False); end Case_Insensitive_Equal; ----------- -- Image -- ----------- function Image (Value : Integer; Min_Width : Integer; Force_Sign : Boolean := False; Padding : Character := '0') return String is S : constant String := Integer'Image (Value); Buf : String (1 .. Integer'Max (S'Length, Min_Width + 1)) := (others => Padding); First : Integer := 2; begin Buf (Buf'Last - S'Length + 2 .. Buf'Last) := S (2 .. S'Last); if Value < 0 then First := 1; Buf (1) := '-'; elsif Force_Sign then First := 1; Buf (1) := '+'; end if; return Buf (First .. Buf'Last); end Image; ------------- -- Replace -- ------------- procedure Replace (S : in out Ada.Strings.Unbounded.Unbounded_String; Pattern : String; Replacement : String) is use Ada.Strings.Unbounded; Ind : Natural := Index_Non_Blank (S); begin while Ind < Length (S) loop Ind := Index (S, Pattern, Ind); exit when Ind = 0; S := Replace_Slice (S, Ind, Ind + Pattern'Length - 1, Replacement); Ind := Ind + Replacement'Length; end loop; end Replace; function Replace (S : String; Pattern : String; Replacement : String) return String is Idx : Natural; begin Idx := Fixed.Index (S, Pattern); if Idx = 0 then return S; else return S (S'First .. Idx - 1) & Replacement & Replace (S => S (Idx + Pattern'Length .. S'Last), Pattern => Pattern, Replacement => Replacement); end if; end Replace; --------------------- -- Count_For_Split -- --------------------- function Count_For_Split (Str : String; On : Character; Omit_Empty_Lines : Boolean := True) return Natural is Count : Natural := 0; function For_Each (Item : String) return Boolean; -------------- -- For_Eash -- -------------- function For_Each (Item : String) return Boolean is begin if not Omit_Empty_Lines or else Item'Length > 0 then Count := Count + 1; end if; return True; end For_Each; begin Split (Str, "" & On, For_Each'Access); return Count; end Count_For_Split; ----------- -- Split -- ----------- procedure Split (Str : String; On : String; For_Each : access function (Item : String) return Boolean) is First : Positive := Str'First; Last : Natural; begin while First <= Str'Last loop Last := Fixed.Index (Str, On, First); if Last = 0 then Last := Str'Last + 1; end if; exit when not For_Each (Str (First .. Last - 1)); First := Last + On'Length; end loop; end Split; ----------- -- Split -- ----------- function Split (Str : String; On : Character; Omit_Empty_Lines : Boolean := True) return GNAT.Strings.String_List_Access is Total : constant Natural := Count_For_Split (Str, On, Omit_Empty_Lines); Result : constant GNAT.Strings.String_List_Access := new GNAT.Strings.String_List (1 .. Total); Count : Positive := 1; function For_Each (Item : String) return Boolean; -------------- -- For_Each -- -------------- function For_Each (Item : String) return Boolean is begin if not Omit_Empty_Lines or else Item'Length > 0 then Result (Count) := new String'(Item); Count := Count + 1; end if; return True; end For_Each; -- Start of processing for Split begin Split (Str, (1 => On), For_Each'Access); return Result; end Split; ----------- -- Split -- ----------- function Split (Str : String; On : Character; Omit_Empty_Lines : Boolean := True) return Unbounded_String_Array is use Ada.Strings.Unbounded; Total : constant Natural := Count_For_Split (Str, On, Omit_Empty_Lines); Result : Unbounded_String_Array (1 .. Total); Count : Positive := 1; function For_Each (Item : String) return Boolean; -------------- -- For_Each -- -------------- function For_Each (Item : String) return Boolean is begin if not Omit_Empty_Lines or else Item'Length > 0 then Result (Count) := To_Unbounded_String (Item); Count := Count + 1; end if; return True; end For_Each; -- Start of processing for Split begin Split (Str, (1 => On), For_Each'Access); return Result; end Split; ---------------- -- Capitalize -- ---------------- function Capitalize (Name : String) return String is Result : String (Name'Range); J : Integer := Result'First; begin for N in Name'Range loop if Name (N) = '+' then Result (J) := 'p'; J := J + 1; elsif Name (N) = '?' then Result (J) := 'U'; J := J + 1; elsif Name (N) = '_' and then N > Name'First and then Name (N - 1) = '_' then null; elsif Name (N) >= ' ' and then Name (N) <= '/' then Result (J) := '_'; J := J + 1; elsif J = Result'First or else Result (J - 1) = '_' then Result (J) := To_Upper (Name (N)); J := J + 1; else Result (J) := To_Lower (Name (N)); J := J + 1; end if; end loop; return Result (Result'First .. J - 1); end Capitalize; --------------- -- Ends_With -- --------------- function Ends_With (Str : String; Suffix : String) return Boolean is pragma Suppress (All_Checks); begin -- This version is slightly faster than checking -- return Tail (File_Name, Suffix'Length) = Suffix; -- which needs a function returning a string. if Str'Length < Suffix'Length then return False; end if; -- Do the loop in reverse, since it's likely that Suffix starts with '.' -- In the GPS case, it is also often the case that suffix starts with -- '.ad' for Ada extensions for J in reverse Suffix'Range loop if Str (Str'Last + J - Suffix'Last) /= Suffix (J) then return False; end if; end loop; return True; end Ends_With; ----------------- -- Starts_With -- ----------------- function Starts_With (Str : String; Prefix : String) return Boolean is pragma Suppress (All_Checks); begin if Str'Length < Prefix'Length then return False; end if; return Str (Str'First .. Str'First + Prefix'Length - 1) = Prefix; end Starts_With; ---------------------------- -- Is_Directory_Separator -- ---------------------------- function Is_Directory_Separator (C : Character) return Boolean is begin -- In addition to the default directory_separator allow the '/' to -- act as separator since this a valid path separator on Windows -- systems. return C = GNAT.OS_Lib.Directory_Separator or else C = '/'; end Is_Directory_Separator; ------------------------- -- Executable_Location -- ------------------------- function Executable_Location return String is Exec_Path : constant String := Executable_Path; Path_Last : Integer := -1; begin -- Find the directory containing the executable for J in reverse Exec_Path'Range loop if Is_Directory_Separator (Exec_Path (J)) then Path_Last := J - 1; exit; end if; end loop; -- Handle special case for which we did not find any directory -- (can occur for some platforms if the path to the executable -- can not be found). if Path_Last = -1 then return ""; end if; -- Check if we can strip bin directory. As some systems are -- case insensitive perform a case insensitive comparison. if Path_Last >= Exec_Path'First + 3 then declare Dir_Element : String := Exec_Path (Path_Last - 2 .. Path_Last); begin GNAT.Case_Util.To_Lower (Dir_Element); if Dir_Element = "bin" and then Is_Directory_Separator (Exec_Path (Path_Last - 3)) then return Exec_Path (Exec_Path'First .. Path_Last - 3); else return Exec_Path (Exec_Path'First .. Path_Last + 1); end if; end; else return Exec_Path (Exec_Path'First .. Path_Last + 1); end if; end Executable_Location; --------------------- -- Executable_Path -- --------------------- function Executable_Path return String is function Internal (Str : System.Address; Length : Integer) return Integer; pragma Import (C, Internal, "c_executable_path"); -- Allocate a buffer of size 32K (maximum path on windows platform when -- prefixing the path with \?. This should also cover Linux and MacOS. Result : String (1 .. 32768); -- Length of returned path by system low level functions Allocated : Integer; begin Allocated := Internal (Result'Address, Result'Length); if Allocated = 0 or else Allocated >= Result'Length then -- If we cannot get the executable name through system API, fallback -- on argv[0] which is less accurate. declare Command_Name : constant String := Ada.Command_Line.Command_Name; begin -- Check if the argument contains some directory information. for Idx in Command_Name'Range loop if Is_Directory_Separator (Command_Name (Idx)) then -- We have some path information. Note that in case the -- path is relative and the application changed the current -- directory then the returned path will be incorrect. return GNAT.OS_Lib.Normalize_Pathname (Command_Name, Resolve_Links => True); end if; end loop; -- If you are here, the user has typed the executable name with no -- directory prefix. -- There is a potential issue here (see K112-046) where -- GNAT.OS_Lib will in fact return any non-executable file found -- in the PATH, whereas shells only consider executable files. -- As a result, the user might end up with a wrong path, not -- matching the one found by the shell. -- PATH variable might also have been modified by application or -- not passed with the same value to the executable itself. declare Executable_Path : String_Access := GNAT.OS_Lib.Locate_Exec_On_Path (Command_Name); begin if Executable_Path /= null then declare Result : constant String := Executable_Path.all; begin Free (Executable_Path); return GNAT.OS_Lib.Normalize_Pathname (Result, Resolve_Links => True); end; else -- In case PATH was modified on launching the app or by the -- app itself we might end in a case in which we cannot get -- the original executable path. In that case return the -- command name. return Command_Name; end if; end; end; else return GNAT.OS_Lib.Normalize_Pathname (Result (Result'First .. Allocated), Resolve_Links => True); end if; end Executable_Path; ----------------- -- Skip_Blanks -- ----------------- procedure Skip_Blanks (Str : String; Index : in out Natural) is begin while Index <= Str'Last and then Is_Whitespace (Str (Index)) loop Index := Index + 1; end loop; end Skip_Blanks; -------------------------- -- Skip_Blanks_Backward -- -------------------------- procedure Skip_Blanks_Backward (Str : String; Index : in out Natural) is begin while Index >= Str'First and then Is_Whitespace (Str (Index)) loop Index := Index - 1; end loop; end Skip_Blanks_Backward; --------------- -- Find_Char -- --------------- function Find_Char (Str : String; Char : Character) return Natural is Last : Natural := Str'First; begin while Last <= Str'Last and then Str (Last) /= Char loop Last := Last + 1; end loop; return Last; end Find_Char; --------- -- EOL -- --------- function EOL (Str : String) return Natural is begin return Find_Char (Str, ASCII.LF); end EOL; ---------- -- Join -- ---------- function Join (Str : String; List : GNAT.Strings.String_List) return String is use Ada.Strings.Unbounded; Result : Unbounded_String; begin for L in List'Range loop if List (L) /= null then if Result /= Null_Unbounded_String then Append (Result, Str); end if; Append (Result, List (L).all); end if; end loop; return To_String (Result); end Join; --------------------- -- Strip_Character -- --------------------- function Strip_Character (Text : String; C : Character) return String is pragma Suppress (All_Checks); To : String (1 .. Text'Length); Index_To : Positive := 1; begin for Index in Text'Range loop if Text (Index) /= C then To (Index_To) := Text (Index); Index_To := Index_To + 1; end if; end loop; return To (1 .. Index_To - 1); end Strip_Character; -------------- -- Strip_CR -- -------------- function Strip_CR (Text : String) return String is begin return Strip_Character (Text, ASCII.CR); end Strip_CR; ------------------------ -- Get_Command_Output -- ------------------------ function Get_Command_Output (Command : access GNAT.Expect.Process_Descriptor'Class) return String is use GNAT.Expect; Output : String_Access := new String (1 .. 1024); -- Buffer used to accumulate standard output from the launched -- command, expanded as necessary during execution. Last : Integer := 0; -- Index of the last used character within Output Status : Integer; begin declare Result : Expect_Match; pragma Unreferenced (Result); begin -- This loop runs until the call to Expect raises Process_Died loop Expect (Command.all, Result, ".+", Timeout => -1); declare NOutput : String_Access; S : constant String := Expect_Out (Command.all); pragma Assert (S'Length > 0); begin -- Expand buffer if we need more space. Note here that we add -- S'Length to ensure that S will fit in the new buffer size. if Last + S'Length > Output'Last then NOutput := new String (1 .. 2 * Output'Last + S'Length); NOutput (Output'Range) := Output.all; Free (Output); -- Here if current buffer size is OK else NOutput := Output; end if; NOutput (Last + 1 .. Last + S'Length) := S; Last := Last + S'Length; Output := NOutput; end; end loop; exception when Process_Died => Close (Command.all, Status); end; if Last = 0 then Free (Output); return ""; end if; declare S : constant String := Output (1 .. Last); begin Free (Output); return S; end; end Get_Command_Output; ---------------- -- Time_Value -- ---------------- function Time_Value (Str : String) return Ada.Calendar.Time is First : Integer := Str'First; Last : Integer := Str'Last; -- When no timezone is specified by the user, UTC is assumed. TZ : Duration := 0.0; TZ_Mark : constant Character_Set := To_Set ("+-"); Subsecs : Duration := 0.0; -- Subseconds begin if Str = "" then return No_Time; end if; -- Do we have the name of the day at the beginning of the string, as in -- Tue, 19 Dec 2006 13:59:04+00 if Str'Length > 4 and then Str (First + 3) = ',' then First := First + 5; end if; -- Check for presence of time zone information in the various formats -- specified by ISO8601. This only applies when the time is also given, -- i.e. the value is long enough (8 chars for time + at least 8 for -- date (01/02/02). if Str'Length > 16 then if Is_In (Str (Last - 2), TZ_Mark) then -- [+-]HH TZ := -Duration'Value (Str (Last - 2 .. Last)) * 3600; Last := Last - 3; elsif Is_In (Str (Last - 4), TZ_Mark) then -- [+-]HHMM TZ := -Duration'Value (Str (Last - 4 .. Last - 2)) * 3600 - Duration'Value (Str (Last - 4) & Str (Last - 1 .. Last)) * 60; Last := Last - 5; elsif Is_In (Str (Last - 5), TZ_Mark) and then Str (Last - 2) = ':' then -- [+-]HH:MM TZ := -Duration'Value (Str (Last - 5 .. Last - 3)) * 3600 - Duration'Value (Str (Last - 5) & Str (Last - 1 .. Last)) * 60; Last := Last - 6; end if; end if; -- Special case: UTC time zone specified as 'Z' if Str'Length > 1 and then Str (Last) = 'Z' then Last := Last - 1; TZ := 0.0; -- date is given as UTC end if; -- Ignore fraction of second for S in reverse First .. Last loop if Str (S) = '.' then Subsecs := Duration'Value (Str (S .. Last)); Last := S - 1; exit; end if; end loop; -- In ISO format, the separator between date and time is 'T', whereas -- GNAT.Calendar.Time_IO expects as space. declare S2 : String := Str (First .. Last); Local : Ada.Calendar.Time; begin for S in S2'Range loop if S2 (S) = 'T' then S2 (S) := ' '; exit; end if; end loop; Local := GNAT.Calendar.Time_IO.Value (S2); -- GNAT.Calendar.Time_IO.Value uses Ada.Calendars.Time_Of, which -- for GNAT assumes the input date is in the local time zone. -- UTC_Time_Offset call is used to compensated that offset. return Local + TZ + UTC_Time_Offset (Local) + Subsecs; end; exception when Constraint_Error => return No_Time; end Time_Value; -------------- -- Truncate -- -------------- function Truncate (Date : Time; Time_Zone : Time_Zones.Time_Offset := 0) return Time is Year : Year_Number; Month : Month_Number; Day : Day_Number; Dum1 : Day_Duration; Dum2 : Boolean; begin Formatting.Split (Date, Year, Month, Day, Dum1, Leap_Second => Dum2, Time_Zone => Time_Zone); return Formatting.Time_Of (Year, Month, Day, Time_Zone => Time_Zone); end Truncate; ---------------- -- Line_Start -- ---------------- function Line_Start (Str : String; P : Natural) return Natural is Index : Natural := Natural'Min (Str'Last, P); begin if P <= Str'First then return P; end if; if Str (Index) = ASCII.LF then Index := Index - 1; if Str (Index) = ASCII.LF then return Index + 1; elsif Str (Index) = ASCII.CR then if Index > Str'First then Index := Index - 1; if Str (Index) = ASCII.LF then return Index + 1; end if; else return Str'First; end if; end if; elsif Str (Index) = ASCII.CR then Index := Index - 1; if Str (Index) = ASCII.LF then return Index + 1; end if; end if; for J in reverse Str'First .. Index loop if Str (J) = ASCII.LF or else Str (J) = ASCII.CR then if J < Str'Last then return J + 1; else return Str'Last; end if; end if; end loop; return Str'First; end Line_Start; -------------- -- Line_End -- -------------- function Line_End (Str : String; P : Natural) return Natural is Index : constant Natural := Natural'Max (Str'First, P); begin for J in Index .. Str'Last loop if Str (J) = ASCII.LF or else Str (J) = ASCII.CR then return J - 1; end if; end loop; return Str'Last; end Line_End; --------------- -- Next_Line -- --------------- function Next_Line (Str : String; P : Natural) return Natural is Index : constant Natural := Natural'Max (Str'First, P); begin for J in Index .. Str'Last - 1 loop if Str (J) = ASCII.LF then return J + 1; end if; end loop; return Str'Last; end Next_Line; ------------------- -- Previous_Line -- ------------------- function Previous_Line (Str : String; P : Natural) return Natural is Index : constant Natural := Line_Start (Str, P); begin if Index > Str'First then return Line_Start (Str, Index - 1); else return Str'First; end if; end Previous_Line; ---------------- -- Skip_Lines -- ---------------- procedure Skip_Lines (Str : String; Lines : Integer; Index : in out Natural; Lines_Skipped : out Natural) is Index_Saved : Natural; begin Lines_Skipped := 0; if Lines >= 0 then while Lines_Skipped < Lines loop Index := Next_Line (Str, Index); if Index = Str'Last then Index := Line_Start (Str, Index); exit; end if; Lines_Skipped := Lines_Skipped + 1; end loop; else Index_Saved := Line_Start (Str, Index); while Lines_Skipped < -Lines loop Index := Previous_Line (Str, Index); exit when Index = Index_Saved; Lines_Skipped := Lines_Skipped + 1; end loop; end if; end Skip_Lines; ------------------- -- Is_Blank_Line -- ------------------- function Is_Blank_Line (Str : String; Index : Natural := 0) return Boolean is It : Natural := Index; begin if It = 0 then It := Str'First; end if; if It >= Str'First then while It <= Str'Last and then Str (It) /= ASCII.CR and then Str (It) /= ASCII.LF loop if Str (It) /= ' ' and then Str (It) /= ASCII.HT then return False; end if; It := It + 1; end loop; end if; return True; end Is_Blank_Line; -------------------- -- Skip_To_String -- -------------------- procedure Skip_To_String (Str : String; Index : in out Natural; Substring : String) is L : constant Natural := Substring'Length - 1; begin while Index + L <= Str'Last and then Str (Index .. Index + L) /= Substring loop Index := Index + 1; end loop; end Skip_To_String; ----------------------- -- Forward_UTF8_Char -- ----------------------- function Forward_UTF8_Char (Str : String; Index : Integer) return Integer is type Unicode_Char is mod 2**32; C : constant Unicode_Char := Character'Pos (Str (Index)); begin -- Compute the length of the encoding given what was in the first byte if C < 128 then return Index + 1; elsif (C and 16#E0#) = 16#C0# then return Index + 2; elsif (C and 16#F0#) = 16#E0# then return Index + 3; elsif (C and 16#F8#) = 16#F0# then return Index + 4; elsif (C and 16#FC#) = 16#F8# then return Index + 5; elsif (C and 16#FE#) = 16#FC# then return Index + 6; else -- Invalid encoding return Index + 1; end if; end Forward_UTF8_Char; -------------------- -- Skip_To_Column -- -------------------- procedure Skip_To_Column (Str : String; Columns : Integer := 0; Index : in out Integer; Tab_Width : Integer := 8) is Current_Col : Integer := 1; begin if Str = "" then return; end if; while Current_Col < Columns and then Natural (Index) <= Str'Last and then Str (Natural (Index)) /= ASCII.LF loop if Natural (Index) < Str'Last and then Str (Natural (Index)) = ASCII.HT then Current_Col := Current_Col + (Tab_Width - (Current_Col - 1) mod Tab_Width); else Current_Col := Current_Col + 1; end if; Index := Forward_UTF8_Char (Str, Natural (Index)); end loop; end Skip_To_Column; --------------- -- Join_Path -- --------------- function Join_Path (Path : String; Path1, Path2, Path3, Path4 : String := "") return String is use String_Builders; Sep : constant Character := GNAT.OS_Lib.Directory_Separator; Result : Static_String_Builder (Path'Length + Path1'Length + Path2'Length + Path3'Length + Path4'Length + 4 + 1); -- Maximum length for the returned path is the sum of paths -- passed as argument, 4 directory separators and 1 character -- to let room for a null character. procedure Append_Path_Element (Path_Elmt : String); -------------------------- -- Append_Path_Element -- -------------------------- procedure Append_Path_Element (Path_Elmt : String) is begin if Path_Elmt /= "" then if GNAT.OS_Lib.Is_Absolute_Path (Path_Elmt) then -- If the element to add is an absolute path then reset the -- result. Set (Result, Path_Elmt); else -- If not at the beginning of the resulting path ensure that a -- directory separator is inserted. if Length (Result) > 0 and then not Is_Directory_Separator (Element (Result, Length (Result))) then Append (Result, Sep); end if; Append (Result, Path_Elmt); end if; end if; end Append_Path_Element; begin Append_Path_Element (Path); Append_Path_Element (Path1); Append_Path_Element (Path2); Append_Path_Element (Path3); Append_Path_Element (Path4); return As_String (Result); end Join_Path; --------------------- -- Add_Search_Path -- --------------------- procedure Add_Search_Path (Variable : String; Path : String) is use String_Builders; package Env renames Ada.Environment_Variables; Original_Value : constant String := Env.Value (Variable, ""); Result : Static_String_Builder (Original_Value'Length + Path'Length + 1 + 1); Start : Integer := Original_Value'First; begin -- Do nothing if path is empty if Path = "" then return; end if; -- Add the new path Set (Result, Path); -- Scan original value and remove any duplicate of the new path for Idx in Original_Value'Range loop if Original_Value (Idx) = GNAT.OS_Lib.Path_Separator then if Original_Value (Start .. Idx - 1) /= Path then Append (Result, GNAT.OS_Lib.Path_Separator); Append (Result, Original_Value (Start .. Idx - 1)); Start := Idx + 1; end if; if Idx = Original_Value'Last then Append (Result, GNAT.OS_Lib.Path_Separator); end if; elsif Idx = Original_Value'Last and then Start <= Original_Value'Last then if Original_Value (Start .. Idx) /= Path then Append (Result, GNAT.OS_Lib.Path_Separator); Append (Result, Original_Value (Start .. Idx)); end if; end if; end loop; Env.Set (Variable, As_String (Result)); end Add_Search_Path; end GNATCOLL.Utils; gnatcoll-core-21.0.0/src/gnatcoll-refcount-weakref.ads0000644000175000017500000001607713661715457022547 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- By definition, an object can never be freed while there are references -- to it. -- However, this simple scheme fails in some cases. For instance, imagine -- you want to cache some refcounted type into a map. The map would then -- own a reference to the object, which is thus never freed while the map -- exists (presumably for the life of your application). -- A solution to this problem is the notion of "weak reference": these act -- as containers that point to the element, without owning a reference to -- them. When the element is destroyed (because its refcount can now reach -- 0), the container is set to a special state that indicates the element -- no longer exists. -- With this scheme, the cache will still contain entries for the elements, -- but those entries will return a Null_Ref when accessed. package GNATCOLL.Refcount.Weakref is pragma Obsolescent (Weakref, "Use GNATCOLL.Refcount.Shared_Pointers"); type Weak_Refcounted is abstract new GNATCOLL.Refcount.Refcounted with private; -- A special refcounted type, which can manipulate weak references overriding procedure Free (Self : in out Weak_Refcounted); -- If you need to override this procedure in your own code, you need to -- make sure you correctly call this inherited procedure. type Proxy is new GNATCOLL.Refcount.Refcounted with record Proxied : Refcounted_Access; end record; package Proxy_Pointers is new Smart_Pointers (Proxy); -- An internal, implementation type. -- -- A weak ref acts as a smart pointed with two level of indirection: -- type My_Type is new GNATCOLL.Refcount.Weakref.Refcounted with ...; -- package P is new Weakref_Pointers (My_Type); -- R : P.Ref; -- WR : P.Weak_Ref; -- R now takes care of the reference counting for R.Data. -- R.Data is an access to My_Type, freed automatically. -- -- WR now takes care of the reference counting for a Proxy, whose Proxied -- is set to R.Data. This does not hold a reference to R.Data. However, -- R.Data holds a reference to the proxy. -- As a result, the proxy is never freed while R.Data exists. But the -- latter can be destroyed even when the proxy exists. generic type Encapsulated is abstract new Weak_Refcounted with private; package Weakref_Pointers is package Pointers is new Smart_Pointers (Encapsulated); subtype Encapsulated_Access is Pointers.Encapsulated_Access; subtype Ref is Pointers.Ref; Null_Ref : constant Ref := Pointers.Null_Ref; procedure Set (Self : in out Ref; Data : Encapsulated'Class) renames Pointers.Set; procedure Set (Self : in out Ref; Data : access Encapsulated'Class) renames Pointers.Set; function Get (P : Ref) return Encapsulated_Access renames Pointers.Get; function "=" (P1, P2 : Ref) return Boolean renames Pointers."="; function "=" (P1, P2 : Pointers.Encapsulated_Access) return Boolean renames Pointers."="; -- The manipulation of the smart pointers subtype Weak_Ref is Proxy_Pointers.Ref; Null_Weak_Ref : constant Weak_Ref := Weak_Ref (Proxy_Pointers.Null_Ref); function "=" (P1, P2 : Weak_Ref) return Boolean renames Proxy_Pointers."="; function Get_Weak_Ref (Self : Ref'Class) return Weak_Ref; -- Return a weak reference to Self. -- It does not hold a reference to Self, which means that Self could be -- destroyed while the weak reference exists. However, this will not -- result -- in a Storage_Error when you access the reference. function Was_Freed (Self : Weak_Ref'Class) return Boolean; -- True if the weakly referenced element was freed (thus Get would -- return Null_Ref). It is more efficient to use this function than -- compare the result of Get with Null_Ref, since the latter will need -- to play with refcounting. function Get (Self : Weak_Ref'Class) return Ref; procedure Get (Self : Weak_Ref'Class; R : out Ref'Class); -- Return the weakly referenced object. This will return Null_Ref -- if the object has already been destroyed. -- The procedure version can be used if you have subclassed Ref. -- The code should look like: -- -- -- Create the smart pointer -- Tmp : Refcounted_Access := new My_Refcounted_Type; -- R : Ref := Allocate (Tmp); -- Hold a ref to Tmp -- -- WRef := Get_Weak_Ref (R); -- Does not hold a ref to Tmp -- -- R := Null_Ref; -- Releases ref to Tmp, and free Tmp -- we now have Get (WRef) = null -- -- In the case of a multitasking application, you must write your code -- so that the referenced type is not freed while you are using it. For -- instance: -- declare -- R : constant Ref := Get (WRef); -- hold a ref to Tmp -- begin -- if R /= Null_Ref then -- ... manipulate R -- Tmp cannot be freed while in the declare block, since we -- own a reference to it -- end if; -- end; end Weakref_Pointers; private type Weak_Refcounted is abstract new GNATCOLL.Refcount.Refcounted with record Proxy : Proxy_Pointers.Ref; -- Hold a reference to a proxy end record; end GNATCOLL.Refcount.Weakref; gnatcoll-core-21.0.0/src/gnatcoll-locks.ads0000644000175000017500000000725013661715457020404 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNAT.Semaphores; use GNAT.Semaphores; private with Ada.Finalization; -- This package provides simple locking primitives on top of GNAT.Semaphores. package GNATCOLL.Locks is subtype Mutual_Exclusion is Binary_Semaphore (Initially_Available => True, Ceiling => Default_Ceiling); -- This subtype represents a Mutual exclusion primitive, which is a common -- use case for Semaphores. type Scoped_Lock (Lock : access Mutual_Exclusion) is limited private; -- This type represents a scoped lock for the Mutual_Exclusion object -- passed as discriminant. It will hold the Mutex as long as the object is -- alive, and release it when it is finalized. -- -- It provides a useful idiom to protect a subprogram or a part of a -- subprogram from data races via a critical section. Here is code without -- a scoped lock:: -- -- A : Integer := 0; -- M : Mutual_Exclusion; -- -- procedure Modify_State is -- begin -- M.Seize; -- A := A + 1; -- if A > 12 then -- M.Release; -- return; -- end if; -- -- A := A + 2; -- M.Release; -- exception -- Addition can overflow! -- when others => -- M.Release; -- raise; -- end Modify_State; -- -- And here is the code with the scoped lock idiom:: -- -- A : Integer := 0; -- M : Mutual_Exclusion; -- -- procedure Modify_State is -- Lock : Scoped_Lock (M); -- begin -- A := A + 1; -- if A > 12 then -- return; -- end if; -- -- A := A + 2; -- end Modify_State; private type Scoped_Lock (Lock : access Mutual_Exclusion) is new Ada.Finalization.Limited_Controlled with null record; overriding procedure Initialize (This : in out Scoped_Lock); overriding procedure Finalize (This : in out Scoped_Lock); end GNATCOLL.Locks; gnatcoll-core-21.0.0/src/gnatcoll-terminal.adb0000644000175000017500000003325013661715457021062 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2005-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Text_IO; use Ada.Text_IO; with GNAT.OS_Lib; use GNAT.OS_Lib; package body GNATCOLL.Terminal is On_Windows : constant Boolean := GNAT.OS_Lib.Directory_Separator = '\'; Color_To_Win32 : constant array (ANSI_Color) of Integer := (Unchanged => -1, Black => 0, Red => 4, Green => 2, Yellow => 6, Blue => 1, Magenta => 5, Cyan => 3, Grey => 7, Reset => -1); Style_To_Win32 : constant array (ANSI_Style) of Integer := (Unchanged => -1, Bright => 16#08#, Dim => 16#00#, -- same as Normal Normal => 16#00#, Reset_All => -1); procedure Win_Set_Console (Self : Terminal_Info'Class; Attrs : Integer); -- Windows-specific implementation to change the attributes of the console procedure Decode_Windows_Attributes (Self : in out Terminal_Info'Class; Attrs : Integer); -- Decode the value of the attributes returned by Windows, into the -- default parameters for the terminal. procedure Auto_Detect_Colors (Self : in out Terminal_Info'Class; Support : Supports_Color); -- Auto-detect whether colors are supported function getConsoleScreenBufferInfo (Stderr : Integer) return Integer; pragma Import (C, getConsoleScreenBufferInfo, "gnatcoll_get_console_screen_buffer_info"); function terminal_has_colors (Fd : File_Descriptor) return Integer; pragma Import (C, terminal_has_colors, "gnatcoll_terminal_has_colors"); -- Whether Fd is a terminal that supports color output ------------------------------- -- Decode_Windows_Attributes -- ------------------------------- procedure Decode_Windows_Attributes (Self : in out Terminal_Info'Class; Attrs : Integer) is type Mod_32 is mod 2 ** 32; A : Mod_32; begin Self.Default_Fore := Black; Self.Default_Back := Grey; Self.Default_Style := Normal; if not On_Windows then return; elsif Attrs = -1 then Self.Colors := Unsupported; else A := Mod_32 (Attrs); case A and 7 is when 0 => Self.Default_Fore := Black; when 1 => Self.Default_Fore := Blue; when 2 => Self.Default_Fore := Green; when 3 => Self.Default_Fore := Cyan; when 4 => Self.Default_Fore := Red; when 5 => Self.Default_Fore := Magenta; when 6 => Self.Default_Fore := Yellow; when others => Self.Default_Fore := Grey; end case; case (A / 16) and 7 is when 0 => Self.Default_Back := Black; when 1 => Self.Default_Back := Blue; when 2 => Self.Default_Back := Green; when 3 => Self.Default_Back := Cyan; when 4 => Self.Default_Back := Red; when 5 => Self.Default_Back := Magenta; when 6 => Self.Default_Back := Yellow; when others => Self.Default_Back := Grey; end case; if (A and 16#08#) /= 0 then Self.Default_Style := Bright; else Self.Default_Style := Normal; end if; end if; end Decode_Windows_Attributes; ------------------------ -- Auto_Detect_Colors -- ------------------------ procedure Auto_Detect_Colors (Self : in out Terminal_Info'Class; Support : Supports_Color) is Env : String_Access; procedure Set_Color_Support; -- Set appropriate color support depend on environment ----------------------- -- Set_Color_Support -- ----------------------- procedure Set_Color_Support is begin if On_Windows then Env := Getenv ("ANSICON"); if Env = null or else Env.all = "" then Self.Colors := WIN32_Sequences; else Self.Colors := ANSI_Sequences; end if; Free (Env); else Self.Colors := ANSI_Sequences; end if; end Set_Color_Support; begin case Support is when No => Self.Colors := Unsupported; when Yes => Set_Color_Support; when Auto => if (Self.FD = Stdout and then terminal_has_colors (Standout) /= 0) or else (Self.FD = Stderr and then terminal_has_colors (Standerr) /= 0) then Set_Color_Support; else Self.Colors := Unsupported; end if; end case; end Auto_Detect_Colors; --------------------- -- Init_For_Stdout -- --------------------- procedure Init_For_Stdout (Self : in out Terminal_Info; Colors : Supports_Color := Auto) is begin Self.FD := Stdout; Auto_Detect_Colors (Self, Colors); Decode_Windows_Attributes (Self, getConsoleScreenBufferInfo (Stderr => 0)); end Init_For_Stdout; --------------------- -- Init_For_Stderr -- --------------------- procedure Init_For_Stderr (Self : in out Terminal_Info; Colors : Supports_Color := Auto) is begin Self.FD := Stderr; Auto_Detect_Colors (Self, Colors); Decode_Windows_Attributes (Self, getConsoleScreenBufferInfo (Stderr => 1)); end Init_For_Stderr; ------------------- -- Init_For_File -- ------------------- procedure Init_For_File (Self : in out Terminal_Info; Colors : Supports_Color := Auto) is begin Self.FD := File; case Colors is when Yes => -- Have to use ANSI sequences, since WIN32 sequences call -- subprograms on the terminal itself. Self.Colors := ANSI_Sequences; when No | Auto => Self.Colors := Unsupported; end case; end Init_For_File; ---------------- -- Has_Colors -- ---------------- function Has_Colors (Self : Terminal_Info) return Boolean is begin return Self.Colors /= Unsupported; end Has_Colors; --------------------- -- Has_ANSI_Colors -- --------------------- function Has_ANSI_Colors (Self : Terminal_Info) return Boolean is begin return Self.Colors = ANSI_Sequences; end Has_ANSI_Colors; --------------------- -- Win_Set_Console -- --------------------- procedure Win_Set_Console (Self : Terminal_Info'Class; Attrs : Integer) is procedure Set_Console_Text_Attribute (Stderr : Integer; Attrs : Integer); pragma Import (C, Set_Console_Text_Attribute, "gnatcoll_set_console_text_attribute"); begin Set_Console_Text_Attribute (Boolean'Pos (Self.FD = Stderr), Attrs); end Win_Set_Console; ------------ -- Set_Fg -- ------------ procedure Set_Fg (Self : in out Terminal_Info; Color : ANSI_Color; Term : Ada.Text_IO.File_Type := Ada.Text_IO.Standard_Output) is begin Set_Color (Self, Term, Color, Unchanged, Unchanged); end Set_Fg; ------------ -- Set_Bg -- ------------ procedure Set_Bg (Self : in out Terminal_Info; Color : ANSI_Color; Term : Ada.Text_IO.File_Type := Ada.Text_IO.Standard_Output) is begin Set_Color (Self, Term, Unchanged, Color, Unchanged); end Set_Bg; --------------- -- Set_Style -- --------------- procedure Set_Style (Self : in out Terminal_Info; Style : ANSI_Style; Term : Ada.Text_IO.File_Type := Ada.Text_IO.Standard_Output) is begin Set_Color (Self, Term, Unchanged, Unchanged, Style); end Set_Style; ----------------------- -- Get_ANSI_Sequence -- ----------------------- function Get_ANSI_Sequence (Style : Full_Style) return String is begin return (case Style.Style is when Unchanged => "", when Bright => ASCII.ESC & "[1m", when Dim => ASCII.ESC & "[2m", when Normal => ASCII.ESC & "[22m", when Reset_All => ASCII.ESC & "[0m" ) & (case Style.Fg is when Unchanged => "", when Black => ASCII.ESC & "[30m", when Red => ASCII.ESC & "[31m", when Green => ASCII.ESC & "[32m", when Yellow => ASCII.ESC & "[33m", when Blue => ASCII.ESC & "[34m", when Magenta => ASCII.ESC & "[35m", when Cyan => ASCII.ESC & "[36m", when Grey => ASCII.ESC & "[37m", when Reset => ASCII.ESC & "[39m" ) & (case Style.Bg is when Unchanged => "", when Black => ASCII.ESC & "[40m", when Red => ASCII.ESC & "[41m", when Green => ASCII.ESC & "[42m", when Yellow => ASCII.ESC & "[43m", when Blue => ASCII.ESC & "[44m", when Magenta => ASCII.ESC & "[45m", when Cyan => ASCII.ESC & "[46m", when Grey => ASCII.ESC & "[47m", when Reset => ASCII.ESC & "[49m" ); end Get_ANSI_Sequence; --------------- -- Set_Color -- --------------- procedure Set_Color (Self : in out Terminal_Info; Term : Ada.Text_IO.File_Type := Ada.Text_IO.Standard_Output; Foreground : ANSI_Color := Unchanged; Background : ANSI_Color := Unchanged; Style : ANSI_Style := Unchanged) is Attrs : Integer := 0; begin case Self.Colors is when Unsupported => null; when ANSI_Sequences => Put (Term, Get_ANSI_Sequence ((Fg => Foreground, Bg => Background, Style => Style))); when WIN32_Sequences => if Style = Reset_All then Self.Style := Self.Default_Style; Self.Fore := Self.Default_Fore; Self.Back := Self.Default_Back; elsif Style /= Unchanged then Self.Style := Style; end if; if Foreground = Reset then Self.Fore := Self.Default_Fore; elsif Foreground /= Unchanged then Self.Fore := Foreground; end if; if Background = Reset then Self.Back := Self.Default_Back; elsif Background /= Unchanged then Self.Back := Background; end if; Attrs := Attrs + Style_To_Win32 (Self.Style) + Color_To_Win32 (Self.Fore) + Color_To_Win32 (Self.Back) * 16; Win_Set_Console (Self, Attrs); end case; end Set_Color; ----------------------- -- Beginning_Of_Line -- ----------------------- procedure Beginning_Of_Line (Self : in out Terminal_Info) is procedure Internal (Stderr : Integer); pragma Import (C, Internal, "gnatcoll_beginning_of_line"); begin if Self.FD = File or else Self.Colors = Unsupported then null; else Internal (Boolean'Pos (Self.FD = Stderr)); end if; end Beginning_Of_Line; -------------------------- -- Clear_To_End_Of_Line -- -------------------------- procedure Clear_To_End_Of_Line (Self : in out Terminal_Info) is procedure Internal (Stderr : Integer); pragma Import (C, Internal, "gnatcoll_clear_to_end_of_line"); begin if Self.FD = File or else Self.Colors = Unsupported then null; else Internal (Boolean'Pos (Self.FD = Stderr)); end if; end Clear_To_End_Of_Line; --------------- -- Get_Width -- --------------- function Get_Width (Self : Terminal_Info) return Integer is function Internal (Stderr : Integer) return Integer; pragma Import (C, Internal, "gnatcoll_terminal_width"); begin if Self.FD = File or else Self.Colors = Unsupported then return -1; else return Internal (Boolean'Pos (Self.FD = Stderr)); end if; end Get_Width; end GNATCOLL.Terminal; gnatcoll-core-21.0.0/src/gnatcoll-io-remote-windows.adb0000644000175000017500000005746713661715457022657 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.Utils; with GNAT.Regpat; use GNAT.Regpat; package body GNATCOLL.IO.Remote.Windows is procedure Free (Args : in out GNAT.OS_Lib.Argument_List); -- Free all strings in Args. ---------- -- Free -- ---------- procedure Free (Args : in out GNAT.OS_Lib.Argument_List) is begin for J in Args'Range loop Free (Args (J)); end loop; end Free; ----------------- -- Current_Dir -- ----------------- function Current_Dir (Exec : access Server_Record'Class) return FS_String is Args : GNAT.OS_Lib.Argument_List := (1 => new String'("echo"), 2 => new String'("%CD%")); Output : GNAT.OS_Lib.String_Access; Status : Boolean; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Status then declare Result : constant FS_String := FS_String (Output.all); begin Free (Output); return Result; end; else return "C:\"; end if; end Current_Dir; -------------- -- Home_Dir -- -------------- function Home_Dir (Exec : access Server_Record'Class) return FS_String is Args : GNAT.OS_Lib.Argument_List := (1 => new String'("echo"), 2 => new String'("%HOME%")); Output : GNAT.OS_Lib.String_Access; Status : Boolean; begin Exec.Execute_Remotely (Args, Output, Status); if not Status then GNAT.OS_Lib.Free (Args (2)); Args (2) := new String'("%USERPROFILE%"); Exec.Execute_Remotely (Args, Output, Status); end if; Free (Args); if Status then declare Result : constant FS_String := FS_String (Output.all); begin Free (Output); return Result; end; else return "C:\"; end if; end Home_Dir; ------------- -- Tmp_Dir -- ------------- function Tmp_Dir (Exec : access Server_Record'Class) return FS_String is Args : GNAT.OS_Lib.Argument_List := (1 => new String'("echo"), 2 => new String'("%TMP%")); Output : GNAT.OS_Lib.String_Access; Status : Boolean; begin Exec.Execute_Remotely (Args, Output, Status); if not Status then GNAT.OS_Lib.Free (Args (2)); Args (2) := new String'("%TMPDIR%"); Exec.Execute_Remotely (Args, Output, Status); end if; Free (Args); if Status then declare Result : constant FS_String := FS_String (Output.all); begin Free (Output); return Result; end; else return "C:\tmp\"; end if; end Tmp_Dir; ------------------------ -- Get_Logical_Drives -- ------------------------ function Get_Logical_Drives (Exec : access Server_Record'Class) return String_List_Access is Status : Boolean; Args : GNAT.OS_Lib.Argument_List := (1 => new String'("vol"), 2 => null, 3 => new String'("2>&1")); Ret : String_List (1 .. 24); Idx : Natural := Ret'First; begin for Drive in Character'('C') .. Character'('Z') loop Args (2) := new String'(Drive & ":"); Exec.Execute_Remotely (Args, Status); Free (Args (2)); if Status then Ret (Idx) := new String'(Drive & ":"); Idx := Idx + 1; end if; end loop; Free (Args); return new String_List'(Ret (1 .. Idx - 1)); end Get_Logical_Drives; -------------------- -- Locate_On_Path -- -------------------- function Locate_On_Path (Exec : access Server_Record'Class; Base : FS_String) return FS_String is function Get_Base return String; function Get_Base return String is begin if Base'Length < 4 or else Base (Base'Last - 3 .. Base'Last) /= ".exe" then return String (Base) & ".exe"; else return String (Base); end if; end Get_Base; Args : GNAT.OS_Lib.Argument_List := (new String'("for"), new String'("/f"), new String'("""usebackq"""), new String'("%i"), new String'("in"), new String'("('" & Get_Base & "')"), new String'("do"), new String'("@echo"), new String'("%~dp$PATH:i%i")); Output : String_Access; Status : Boolean; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Status then if Output.all /= Get_Base then declare -- Don't try to translate the string into a directory, as this -- is all handled later at VFS level. Result : constant FS_String := FS_String (Output.all); begin Free (Output); return Result; end; else Free (Output); return ""; end if; else return ""; end if; end Locate_On_Path; --------------------- -- Is_Regular_File -- --------------------- function Is_Regular_File (Exec : access Server_Record'Class; File : FS_String) return Boolean is -- Redirect stderr to stdout for synchronisation purpose -- (stderr is asynchronous on windows) Args : GNAT.OS_Lib.Argument_List := (new String'("dir"), new String'("/a-d"), new String'("""" & String (File) & """"), new String'("2>&1")); Status : Boolean; begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Is_Regular_File; ---------- -- Size -- ---------- function Size (Exec : access Server_Record'Class; File : FS_String) return Long_Integer is Args : GNAT.OS_Lib.Argument_List := (new String'("dir"), new String'("/-C"), new String'("""" & String (File) & """"), new String'("2>&1")); Status : Boolean; Size : Long_Integer := 0; Output : GNAT.Strings.String_Access; S : GNAT.Strings.String_List_Access; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Status and Output /= null then S := GNATCOLL.Utils.Split (Output.all, ' '); begin Size := Long_Integer'Value (S (S'First + 2).all); exception when Constraint_Error => Size := 0; end; Free (S); end if; Free (Output); return Size; end Size; ------------------ -- Is_Directory -- ------------------ function Is_Directory (Exec : access Server_Record'Class; File : FS_String) return Boolean is Args : GNAT.OS_Lib.Argument_List := (new String'("dir"), new String'("/ad"), new String'("""" & String (File) & """"), new String'("2>&1")); Status : Boolean; begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Is_Directory; ---------------------- -- Is_Symbolic_Link -- ---------------------- function Is_Symbolic_Link (Exec : access Server_Record'Class; File : FS_String) return Boolean is pragma Unreferenced (Exec, File); begin -- ??? There are now symbolic links on Windows (Vista, Server 2008). -- Should we handle them ? return False; end Is_Symbolic_Link; --------------------- -- File_Time_Stamp -- --------------------- function File_Time_Stamp (Exec : access Server_Record'Class; File : FS_String) return Ada.Calendar.Time is Query_Args : GNAT.OS_Lib.Argument_List := (new String'("reg"), new String'("query"), new String'("""HKCU\Control Panel\International"""), new String'("/v"), new String'("sShortDate")); Query_Regexp : constant Pattern_Matcher := Compile ("REG_SZ\s+([dMy]+)[^dMy]*([dMy]+)[^dMy]*([dMy]+)$"); Regexp : constant Pattern_Matcher := Compile ("(\d+)[^\d](\d+)[^\d](\d+)\s+(\d\d:\d\d)\s+"); Args : GNAT.OS_Lib.Argument_List := (new String'("dir"), new String'("/tw"), new String'("/4"), new String'("""" & String (File) & """"), new String'("2>"), new String'("/dev/null")); Status : Boolean; Matched : Match_Array (0 .. 4); Output : String_Access; Year : Natural; Month : Natural; Day : Natural; Hour : Natural; Minute : Natural; Y_Pos : Natural := 0; M_Pos : Natural := 0; D_Pos : Natural := 0; use Ada.Calendar; begin -- We need to first query the date format -- (dd/MM/ yyyy, yyyy-mm-dd, mm-dd-yyyy ?) Exec.Execute_Remotely (Query_Args, Output, Status); Free (Query_Args); if Status then Match (Query_Regexp, Output.all, Matched); if Matched (0) /= No_Match then for J in 1 .. 3 loop if Output (Matched (J).First) = 'd' then D_Pos := J; elsif Output (Matched (J).First) = 'M' then M_Pos := J; elsif Output (Matched (J).First) = 'y' then Y_Pos := J; end if; end loop; end if; end if; Free (Output); if D_Pos = 0 or else M_Pos = 0 or else Y_Pos = 0 then return GNATCOLL.Utils.No_Time; end if; Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Status then Match (Regexp, Output.all, Matched); if Matched (0) = No_Match then return GNATCOLL.Utils.No_Time; end if; Year := Natural'Value (Output (Matched (Y_Pos).First .. Matched (Y_Pos).Last)); Month := Natural'Value (Output (Matched (M_Pos).First .. Matched (M_Pos).Last)); Day := Natural'Value (Output (Matched (D_Pos).First .. Matched (D_Pos).Last)); Hour := Natural'Value (Output (Matched (4).First .. Matched (4).First + 1)); Minute := Natural'Value (Output (Matched (4).First + 3 .. Matched (4).First + 4)); Free (Output); return Ada.Calendar.Time_Of (Year, Month, Day, Seconds => 60.0 * 60.0 * Day_Duration (Hour) + 60.0 * Day_Duration (Minute)); end if; Free (Output); return GNATCOLL.Utils.No_Time; exception when others => return GNATCOLL.Utils.No_Time; end File_Time_Stamp; ----------------- -- Is_Readable -- ----------------- function Is_Readable (Exec : access Server_Record'Class; File : FS_String) return Boolean is pragma Unreferenced (Exec, File); begin -- A file cannot be unreadable on Windows, unless you use ACL (which we -- don't here) return True; end Is_Readable; ----------------- -- Is_Writable -- ----------------- function Is_Writable (Exec : access Server_Record'Class; File : FS_String) return Boolean is Args : GNAT.OS_Lib.Argument_List := (new String'("dir"), new String'("/a-r"), new String'("""" & String (File) & """")); Status : Boolean; begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Is_Writable; ------------------ -- Set_Writable -- ------------------ procedure Set_Writable (Exec : access Server_Record'Class; File : FS_String; State : Boolean) is Args : GNAT.OS_Lib.Argument_List := (new String'("attrib"), new String'("-r"), new String'("""" & String (File) & """"), new String'("2>&1")); Status : Boolean; pragma Unreferenced (Status); begin if not State then Args (2) (Args (2)'First) := '+'; end if; Exec.Execute_Remotely (Args, Status); Free (Args); end Set_Writable; ------------------ -- Set_Readable -- ------------------ procedure Set_Readable (Exec : access Server_Record'Class; File : FS_String; State : Boolean) is pragma Unreferenced (Exec, File, State); begin -- A file cannot be unreadable on Windows, unless you use ACL (which we -- don't here) return; end Set_Readable; ------------ -- Rename -- ------------ procedure Rename (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean) is Args : GNAT.OS_Lib.Argument_List := (new String'("ren"), new String'("""" & String (From) & """"), new String'("""" & String (Dest) & """"), new String'("2>&1")); begin Exec.Execute_Remotely (Args, Success); Free (Args); end Rename; ---------- -- Copy -- ---------- procedure Copy (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean) is Args : GNAT.OS_Lib.Argument_List := (new String'("copy"), new String'("""" & String (From) & """"), new String'("""" & String (Dest) & """"), new String'("2>&1")); begin Exec.Execute_Remotely (Args, Success); Free (Args); end Copy; ------------ -- Delete -- ------------ procedure Delete (Exec : access Server_Record'Class; File : FS_String; Success : out Boolean) is Args : GNAT.OS_Lib.Argument_List := (new String'("erase"), new String'("/f"), new String'("""" & String (File) & """"), new String'("2>&1")); begin Exec.Execute_Remotely (Args, Success); Free (Args); end Delete; --------------------- -- Read_Whole_File -- --------------------- function Read_Whole_File (Exec : access Server_Record'Class; File : FS_String) return GNAT.Strings.String_Access is Args : GNAT.OS_Lib.Argument_List := (new String'("type"), new String'("""" & String (File) & """")); Status : Boolean; Output : String_Access; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); return Output; end Read_Whole_File; --------------------- -- Read_Whole_File -- --------------------- function Read_Whole_File (Exec : access Server_Record'Class; File : FS_String) return GNATCOLL.Strings.XString is Args : GNAT.OS_Lib.Argument_List := (new String'("type"), new String'("""" & String (File) & """")); Status : Boolean; Output : String_Access; Result : XString; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); Result.Set (Output.all); -- Needs a copy of the string Free (Output); return Result; end Read_Whole_File; ---------------- -- Write_File -- ---------------- function Write_File (Exec : access Server_Record'Class; File : FS_String; Content : String) return Boolean is Success : Boolean := True; procedure Internal_Write (S : String; First_Line : Boolean); -- Write a single line procedure Internal_Write (S : String; First_Line : Boolean) is Args : GNAT.OS_Lib.Argument_List := (new String'("echo"), new String'(S), new String'(">>"), new String'("""" & String (File) & """")); begin if First_Line then Args (3).all := "> "; end if; Exec.Execute_Remotely (Args, Success); Free (Args); end Internal_Write; Last : Natural := Content'First; Idx : Natural := Content'First; begin while Idx <= Content'Last loop if Content (Idx) = ASCII.CR and then Content (Idx + 1) = ASCII.LF then Internal_Write (Content (Last .. Idx - 1), Last = Content'First); exit when not Success; Last := Idx + 2; Idx := Idx + 2; elsif Content (Idx) = ASCII.LF then Internal_Write (Content (Last .. Idx - 1), Last = Content'First); exit when not Success; Last := Idx + 1; Idx := Idx + 1; elsif Idx = Content'Last then Internal_Write (Content (Last .. Idx), Last = Content'First); exit when not Success; else Idx := Idx + 1; end if; end loop; return Success; end Write_File; ---------------- -- Change_Dir -- ---------------- function Change_Dir (Exec : access Server_Record'Class; Dir : FS_String) return Boolean is Args : GNAT.OS_Lib.Argument_List := (new String'("cd"), new String'("""" & String (Dir) & """")); Status : Boolean; begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Change_Dir; -------------- -- Read_Dir -- -------------- function Read_Dir (Exec : access Server_Record'Class; Dir : FS_String; Dirs_Only : Boolean := False; Files_Only : Boolean := False) return GNAT.Strings.String_List is function Create_Args return GNAT.OS_Lib.Argument_List; -- Return dir arguments following the Dirs_Only and Files_Only arguments ----------------- -- Create_Args -- ----------------- function Create_Args return GNAT.OS_Lib.Argument_List is begin if Dirs_Only then return (new String'("dir"), new String'("/ad"), new String'("/b"), new String'(String (Dir)), new String'("2>&1")); elsif Files_Only then return (new String'("dir"), new String'("/a-d"), new String'("/b"), new String'(String (Dir)), new String'("2>&1")); else return (new String'("dir"), new String'("/b"), new String'(String (Dir)), new String'("2>&1")); end if; end Create_Args; Args : GNAT.OS_Lib.Argument_List := Create_Args; Status : Boolean; Output : String_Access; Regexp : constant Pattern_Matcher := Compile ("^(.+)$", Multiple_Lines); Matched : Match_Array (0 .. 1); Index : Natural; Nb_Files : Natural; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Status then Index := Output'First; Nb_Files := 0; while Index <= Output'Last loop Match (Regexp, Output.all, Matched, Index); exit when Matched (0) = No_Match; Index := Matched (1).Last + 1; if Output (Matched (1).First .. Matched (1).Last) /= "." and then Output (Matched (1).First .. Matched (1).Last) /= ".." then Nb_Files := Nb_Files + 1; end if; end loop; declare List : String_List (1 .. Nb_Files); File_Idx : Natural; begin Index := Output'First; File_Idx := List'First; while Index /= Output'Last loop Match (Regexp, Output.all, Matched, Index); exit when Matched (0) = No_Match; Index := Matched (1).Last + 1; if Output (Matched (1).First .. Matched (1).Last) /= "." and then Output (Matched (1).First .. Matched (1).Last) /= ".." then List (File_Idx) := new String' (Output (Matched (1).First .. Matched (1).Last)); File_Idx := File_Idx + 1; end if; end loop; return List; end; end if; return (1 .. 0 => null); end Read_Dir; -------------- -- Make_Dir -- -------------- function Make_Dir (Exec : access Server_Record'Class; Dir : FS_String; Recursive : Boolean) return Boolean is Args : GNAT.OS_Lib.Argument_List := (new String'("mkdir"), new String'("""" & String (Dir) & """"), new String'("2>&1")); Status : Boolean; pragma Unreferenced (Recursive); -- There is no non-recursive mkdir on Windows begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Make_Dir; -------------- -- Copy_Dir -- -------------- procedure Copy_Dir (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean) is begin -- Generated stub: replace with real body! raise Program_Error; end Copy_Dir; ---------------- -- Delete_Dir -- ---------------- procedure Delete_Dir (Exec : access Server_Record'Class; Dir : FS_String; Recursive : Boolean; Success : out Boolean) is Args : GNAT.OS_Lib.Argument_List := (1 => new String'("rmdir"), 2 => new String'("/q"), 3 => new String'("""" & String (Dir) & """"), 4 => new String'("2>&1")); begin if Recursive then Free (Args (2)); Args (2) := new String'("/q/s"); end if; Exec.Execute_Remotely (Args, Success); Free (Args); end Delete_Dir; end GNATCOLL.IO.Remote.Windows; gnatcoll-core-21.0.0/src/run_path_option.c0000644000175000017500000000346113661715457020353 0ustar nicolasnicolas/*---------------------------------------------------------------------------- -- G N A T C O L L -- -- -- -- Copyright (C) 2002-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -----------------------------------------------------------------------------*/ /* Dummy version of run_path_option, needed by mlib.adb */ const char *__gnat_run_path_option = ""; gnatcoll-core-21.0.0/src/gnatcoll-projects-krunch.ads0000644000175000017500000000407313661715457022412 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2015-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides a copy of Krunch procedure. private package GNATCOLL.Projects.Krunch is procedure Krunch (Buffer : in out String; Len : in out Natural; Maxlen : Natural; No_Predef : Boolean); -- A hard copy of procedure Krunch added to avoid dependency on gnat_util. end GNATCOLL.Projects.Krunch; gnatcoll-core-21.0.0/src/gnatcoll-boyer_moore.adb0000644000175000017500000003063513661715457021574 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2001-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- The search is done right-to-left. In the best cases (the text doesn't -- contain any character from the pattern), this results in -- string_length / pattern_length, characters being examined, instead of -- string_length characters. -- -- Compiling the pattern generates two lookup-tables: -- -- The Last Occurrence Function -- ============================ -- -- The Last-Ocurrence-Function returns the right-most location for each -- character in the alphabet in the pattern. -- When a character is seen in the searched string, this array will suggest -- the offset by which we should move the character: -- string: "revolution in the treatment of" -- pattern: " reminiscence" -- ^ when we see the 'h', we can move to: -- " reminiscence" -- -- string: "written notice that" -- pattern: " reminiscence" -- ^ when the see the 'i', we can move to: -- " reminiscence" -- -- string: "golden fleece of" -- pattern: " reminiscence" -- ^ when we see the 'e', no move can be suggested, -- since 'e' appears at the right-most position -- in the pattern. -- -- The Good Suffix Function -- ======================== -- -- This function reports the least amount that garantees that any pattern -- characters that align with the good suffix previously found in the text -- will match those suffix characters. -- For instance: -- -- string: "written notice that" -- pattern: " reminiscence" -- ^ The pattern would be moved so that the "ce" -- vv we have already found match some text. -- " reminiscence" -- -- Combination -- =========== -- -- The two functions above can be computed statically based only on the -- pattern, and without any knowledge of the text. -- When we try to match a pattern with a text, these two functions are -- combined, and the pattern is moved forward by the maximum amount reported -- by the two functions. with Unchecked_Deallocation; with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; with GNAT.Case_Util; use GNAT.Case_Util; package body GNATCOLL.Boyer_Moore is Debug : constant Boolean := False; Debug_Run : constant Boolean := False; procedure Dump_Str (Str : String); -- Print string, replacing the newlines with spaces for clarity procedure Dump (M, Shift, J : Natural; Num_Comp : in out Natural; Motif : Pattern; In_String : String); -- Print the current state of the search. -- The parameters are the internal state in Search. We do not use a -- nested subprogram for efficiency reasons ------------- -- Compile -- ------------- procedure Compile (Motif : in out Pattern; From_String : String; Case_Sensitive : Boolean := True) is -- Prefix contains the following: -- Prefix (J) is the length of the longest prefix of Motif -- which is also a suffix of -- Motif (Motif'First .. Motif'First + J - 1) -- ie of the motif made of the j-th first characters of Motif -- -- Reverse_Prefix is the Prefix function applied to the reverse of Motif -- -- Motif.Last_Occurrence contains the index of the last occurrence of -- the character in the motif. This is in the range 1 .. Motif'Length -- -- Good_Suffix at index J: -- If a mismatch occurs in the j-th character of the pattern, we -- can safely advance by good_suffix (j). -- m = Motif'Length -- GS(J) = m -- - Max (k; 0<=k 0); Motif.Motif := new String (1 .. From_String'Length); Motif.Motif.all := From_String; if not Case_Sensitive then To_Lower (Motif.Motif.all); end if; Prefix (Prefix'First) := 0; Reverse_Prefix (Reverse_Prefix'First) := 0; Motif.Last_Occurrence (Motif.Motif (1)) := 1; for Q in 2 .. Motif.Motif'Last loop -- Compute Last occurrence Motif.Last_Occurrence (Motif.Motif (Q)) := Q; -- Compute prefix function while K > 0 and then Motif.Motif (K + 1) /= Motif.Motif (Q) loop K := Prefix (K); end loop; if Motif.Motif (K + 1) = Motif.Motif (Q) then K := K + 1; end if; Prefix (Q) := K; -- Compute the reverse prefix function while K2 > 0 and then Motif.Motif (Motif.Motif'Last - K2) /= Motif.Motif (Motif.Motif'Last + 1 - Q) loop K2 := Reverse_Prefix (K2); end loop; if Motif.Motif (Motif.Motif'Last - K2) = Motif.Motif (Motif.Motif'Last + 1 - Q) then K2 := K2 + 1; end if; Reverse_Prefix (Q) := K2; end loop; -- Compute the good suffix function K := From_String'Length - Prefix (From_String'Length); Motif.Good_Suffix := new Offset_Array'(0 .. From_String'Length => K); for L in Motif.Motif'Range loop K := From_String'Length - Reverse_Prefix (L); Tmp := L - Reverse_Prefix (L); if Motif.Good_Suffix (K) > Tmp then Motif.Good_Suffix (K) := Tmp; end if; end loop; if Debug then Put (" i = "); for J in Motif.Motif'Range loop Put (Item => J, Width => 3); end loop; New_Line; Put (" Pat[i]= "); for J in Motif.Motif'Range loop Put (" " & Motif.Motif (J)); end loop; New_Line; Put (" Pre[i]= "); for J in Prefix'Range loop Put (Item => Prefix (J), Width => 3); end loop; New_Line; Put ("RevPre[i]= "); for J in Reverse_Prefix'Range loop Put (Item => Reverse_Prefix (J), Width => 3); end loop; New_Line; Put ("GoodSu[i]= "); for J in Motif.Good_Suffix'Range loop Put (Item => Motif.Good_Suffix (J), Width => 3); end loop; New_Line; end if; end Compile; ---------- -- Free -- ---------- procedure Free (Motif : in out Pattern) is procedure Internal is new Unchecked_Deallocation (Offset_Array, Offset_Array_Access); procedure Internal is new Unchecked_Deallocation (String, String_Access); begin Internal (Motif.Good_Suffix); Internal (Motif.Motif); end Free; -------------- -- Dump_Str -- -------------- procedure Dump_Str (Str : String) is begin for S in Str'Range loop if Str (S) = ASCII.LF then Put (' '); else Put (Str (S)); end if; end loop; New_Line; end Dump_Str; ---------- -- Dump -- ---------- procedure Dump (M, Shift, J : Natural; Num_Comp : in out Natural; Motif : Pattern; In_String : String) is begin -- Show current automaton state Num_Comp := Num_Comp + M - J + 1; if Debug_Run then New_Line; Put_Line ("Offset : Shift+j=" & Integer'Image (Shift + J) & " J=" & J'Img & " Last_Occ=" & In_String (Shift + J) & " Max (" & Motif.Good_Suffix (J)'Img & "," & Integer'Image (J - Motif.Last_Occurrence (In_String (Shift + J))) & ")"); if In_String'Length < 400 then Dump_Str (In_String); Put ((1 .. Shift - In_String'First + 1 => ' ')); end if; Dump_Str (Motif.Motif.all); if Shift + J - In_String'First < 400 then Put ((1 .. Shift + J - In_String'First => ' ')); Put_Line ("^"); end if; end if; if J = 0 then Put_Line ("Matched at position" & Natural'Image (Shift + 1) & " after" & Num_Comp'Img & " comparisons"); end if; end Dump; ------------ -- Search -- ------------ function Search (Motif : Pattern; In_String : String) return Integer is M : Natural; Shift : Natural := In_String'First - 1; J : Natural; Num_Comp : Natural := 0; begin if Motif.Motif = null then return -1; end if; M := Motif.Motif'Length; -- length of pattern pragma Assert (Motif.Motif'First = 1); if not Motif.Case_Sensitive then while Shift <= In_String'Last - M loop J := M; while J > 0 and then Motif.Motif (J) = To_Lower (In_String (Shift + J)) loop J := J - 1; end loop; if J = 0 then return Shift + 1; elsif Debug then Dump (M, Shift, J, Num_Comp, Motif, In_String); end if; Shift := Shift + Natural'Max (Motif.Good_Suffix (J), J - Motif.Last_Occurrence (To_Lower (In_String (Shift + J)))); end loop; else while Shift <= In_String'Last - M loop J := M; while J > 0 and then Motif.Motif (J) = In_String (Shift + J) loop J := J - 1; end loop; if J = 0 then return Shift + 1; elsif Debug then Dump (M, Shift, J, Num_Comp, Motif, In_String); end if; Shift := Shift + Natural'Max (Motif.Good_Suffix (J), J - Motif.Last_Occurrence (In_String (Shift + J))); end loop; end if; return -1; end Search; end GNATCOLL.Boyer_Moore; gnatcoll-core-21.0.0/src/gnatcoll-storage_pools-headers.ads0000644000175000017500000001524013661715457023560 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2015-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides storage pools that allocate enough memory for their -- element and an extra header. -- This header can be used to store extra information. For instance, it has -- been used to store a reference counter, or pointers to the next element -- in a container. -- The goal is to reduce the number of memory allocations: instead of doing -- one allocation for the element, and a second one for the counter, we can -- do a single allocation, which is much faster. -- -- Usage example: -- type Header is record -- Refcount : Natural; -- end record; -- package Pools is new Header_Pools (Header); -- package String_Pools is new Pools.Typed (String); -- package Integer_Pools is new Pools.Typed (Integer); -- -- Str : String_Pools.Element_Access; -- Str := new String'("foo"); -- uses the storage pool -- -- String_Pools.Header_Of (Str).Refcount := 1; -- String_Pools.Free (Str); -- reclaim memory pragma Ada_2012; with Ada.Unchecked_Deallocation; with System.Storage_Pools; use System.Storage_Pools; with System.Storage_Elements; use System.Storage_Elements; package GNATCOLL.Storage_Pools.Headers is ------------------ -- Header_Pools -- ------------------ -- The actual memory layout that we need to allocate is described below. In -- all cases, we had a "Pad" (padding) which is used to obey the requested -- alignment for the object. -- Currently, this pool doesn't support alignment clauses (and the generic -- Typed package below doesn't declare any), so the padding is always 0 -- bytes. -- * For a scalar, record, tagged record or constrained array: -- +--------+------+-----------------------+ -- | Header | Pad | Element | -- +--------+------+-----------------------+ -- * For an unconstrained array, whether we use a standard access -- type or a flattened access type (a representation clause gives -- it a size of a standard pointer) -- -- +--------+------+-----------------------+ -- | Header | Pad | First+Last+Element | -- +--------+------+-----------------------+ -- First and Last are the bounds of the array. -- Our pool should return the address of First, and the compiler -- automatically deduces the address of Element to return to the -- user code. -- * For a controlled type: -- -- 1 2 3 -- +--------+------+-----------------------+ -- | Header | Pad | Previous+Next+Element | -- +--------+------+-----------------------+ -- Previous and Next are pointers to other controlled types. -- In code like: -- A := new ...; -- the header pool allocates memory at 1 via malloc, but -- returns 2 to the compiler -- then the compiler stores 3 in A. -- Conversely, when calling Free, the compiler converts A back to -- 2, and our pool converts this back to 1 before calling free() -- The trouble is that when we call "Header_Of" on A, we receive -- the address 3, so it is harder to find 1. -- -- See System.Storage_Pools.Subpools.Allocate_Any_Controlled. generic type Extra_Header is private; -- The header to allocate for each element. The pool will make sure -- to pad its size so that the element's data is properly aligned. type Header_Access is access all Extra_Header; package Header_Pools is type Header_Pool is new Root_Storage_Pool with null record; overriding procedure Allocate (Self : in out Header_Pool; Addr : out System.Address; Size : Storage_Count; Alignment : Storage_Count); overriding procedure Deallocate (Self : in out Header_Pool; Addr : System.Address; Size : Storage_Count; Alignment : Storage_Count); overriding function Storage_Size (Self : Header_Pool) return Storage_Count is (Storage_Count'Last) with Inline; Pool : Header_Pool; ----------- -- Typed -- ----------- generic type Element_Type (<>) is limited private; package Typed is type Element_Access is access all Element_Type; for Element_Access'Size use Standard'Address_Size; for Element_Access'Storage_Pool use Pool; -- Force array bounds to be stored before the array's data, rather -- than as a separate dope vector. function Header_Of (Element : Element_Access) return Header_Access with Inline; -- Points to the beginning of the header for Element. -- Returns null if Element is null procedure Free is new Ada.Unchecked_Deallocation (Element_Type, Element_Access); -- Free the memory used by Element end Typed; end Header_Pools; end GNATCOLL.Storage_Pools.Headers; gnatcoll-core-21.0.0/src/gnatcoll-scripts-projects.adb0000644000175000017500000005402413661715457022567 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Characters.Handling; use Ada.Characters.Handling; with GNAT.Strings; with GNAT.OS_Lib; use GNAT.OS_Lib; with GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Utils; use GNATCOLL.Utils; with GNATCOLL.Scripts.Files; package body GNATCOLL.Scripts.Projects is type Project_Properties_Record is new Instance_Property_Record with record Project : Project_Type; end record; Project_Class_Name : constant String := "Project"; Name_Cst : aliased constant String := "name"; Recursive_Cst : aliased constant String := "recursive"; Attribute_Cst : aliased constant String := "attribute"; Package_Cst : aliased constant String := "package"; Prefix_Cst : aliased constant String := "prefix"; Index_Cst : aliased constant String := "index"; Sources_Cmd_Parameters : constant GNATCOLL.Scripts.Cst_Argument_List := (1 => Recursive_Cst'Access); Source_Dirs_Cmd_Parameters : constant GNATCOLL.Scripts.Cst_Argument_List := (1 => Recursive_Cst'Access); Languages_Cmd_Parameters : constant GNATCOLL.Scripts.Cst_Argument_List := (1 => Recursive_Cst'Access); Project_Cmd_Parameters : constant Cst_Argument_List := (1 => Name_Cst'Access); Get_Attributes_Parameters : constant Cst_Argument_List := (1 => Attribute_Cst'Unchecked_Access, 2 => Package_Cst'Unchecked_Access, 3 => Index_Cst'Unchecked_Access); Scenar_Var_Parameters : constant Cst_Argument_List := (1 => Prefix_Cst'Access); type Project_Tree_Retriever_Access is access all Project_Tree_Retriever'Class; Retriever : Project_Tree_Retriever_Access; procedure Project_Command_Handler (Data : in out Callback_Data'Class; Command : String); procedure Project_Queries (Data : in out Callback_Data'Class; Command : String); procedure Set_Data (Instance : Class_Instance; Project : Project_Type); function Scenario_Variables_Cmd_Line (Prefix : String) return String; -- Return the command line to use to set up the scenario variables when -- calling an external tool that handles project files. -- For a Makefile, set Prefix to "", for gnatmake set prefix to "-X". -- This function returns a concatenation of Prefix & "VAR=VALUE". -------------------- -- Create_Project -- -------------------- function Create_Project (Script : access Scripting_Language_Record'Class; Project : GNATCOLL.Projects.Project_Type) return Class_Instance is Instance : Class_Instance := No_Class_Instance; begin if Project /= No_Project then Instance := New_Instance (Script, New_Class (Get_Repository (Script), Project_Class_Name)); Set_Data (Instance, Project); end if; return Instance; end Create_Project; -------------- -- Get_Data -- -------------- function Get_Data (Data : Callback_Data'Class; N : Positive) return GNATCOLL.Projects.Project_Type is Class : constant Class_Type := Get_Project_Class (Get_Repository (Data)); Inst : constant Class_Instance := Nth_Arg (Data, N, Class, Allow_Null => True); Value : Instance_Property; begin if Inst = No_Class_Instance then return No_Project; end if; Value := Get_Data (Inst, Project_Class_Name); if Value = null then return No_Project; else return Project_Properties_Record (Value.all).Project; end if; end Get_Data; ----------------------- -- Get_Project_Class -- ----------------------- function Get_Project_Class (Repo : access Scripts_Repository_Record'Class) return Class_Type is begin return New_Class (Repo, Project_Class_Name); end Get_Project_Class; ----------------------------- -- Project_Command_Handler -- ----------------------------- procedure Project_Command_Handler (Data : in out Callback_Data'Class; Command : String) is Repo : constant Scripts_Repository := Get_Repository (Data); Instance : Class_Instance; Project : Project_Type; begin if Command = Constructor_Method then Name_Parameters (Data, Project_Cmd_Parameters); Project := Project_Tree.Project_From_Name (Nth_Arg (Data, 2)); if Project = No_Project then Set_Error_Msg (Data, "No such project: " & Nth_Arg (Data, 2)); else Instance := Nth_Arg (Data, 1, Get_Project_Class (Repo)); Set_Data (Instance, Project); end if; elsif Command = "root" then Set_Return_Value (Data, Create_Project (Get_Script (Data), Project_Tree.Root_Project)); elsif Command = "name" then Project := Get_Data (Data, 1); Set_Return_Value (Data, Project.Name); elsif Command = "file" then Project := Get_Data (Data, 1); Set_Return_Value (Data, GNATCOLL.Scripts.Files.Create_File (Get_Script (Data), Project_Path (Project))); elsif Command = "ancestor_deps" then declare Iter : Project_Iterator; P : Project_Type; begin Project := Get_Data (Data, 1); Set_Return_Value_As_List (Data); Iter := Find_All_Projects_Importing (Project, Include_Self => True); loop P := Current (Iter); exit when P = No_Project; Set_Return_Value (Data, Create_Project (Get_Script (Data), P)); Next (Iter); end loop; end; elsif Command = "dependencies" then Name_Parameters (Data, (1 => Recursive_Cst'Access)); declare Recursive : constant Boolean := Nth_Arg (Data, 2, False); Iter : Project_Iterator; P : Project_Type; begin Project := Get_Data (Data, 1); Set_Return_Value_As_List (Data); Iter := Start (Project, Recursive => True, Direct_Only => not Recursive); loop P := Current (Iter); exit when P = No_Project; Set_Return_Value (Data, Create_Project (Get_Script (Data), P)); Next (Iter); end loop; end; elsif Command = "get_attribute_as_list" then Name_Parameters (Data, Get_Attributes_Parameters); declare Project : constant Project_Type := Get_Data (Data, 1); Attr : constant String := Nth_Arg (Data, 2); Pkg : constant String := Nth_Arg (Data, 3, ""); Index : constant String := Nth_Arg (Data, 4, ""); List : String_List_Access := Project.Attribute_Value (Attribute_Pkg_List'(Build (Pkg, Attr)), Index); Value : constant String := Project.Attribute_Value (Attribute_Pkg_String'(Build (Pkg, Attr)), Default => "", Index => Index, Use_Extended => True); begin Set_Return_Value_As_List (Data); if List = null and then Value /= "" then Set_Return_Value (Data, Value); elsif List /= null then for L in List'Range loop Set_Return_Value (Data, List (L).all); end loop; end if; Free (List); end; elsif Command = "get_attribute_as_string" then Name_Parameters (Data, Get_Attributes_Parameters); declare Project : constant Project_Type := Get_Data (Data, 1); Attr : constant String := Nth_Arg (Data, 2); Pkg : constant String := Nth_Arg (Data, 3, ""); Index : constant String := Nth_Arg (Data, 4, ""); Value : constant String := Project.Attribute_Value (Attribute_Pkg_String'(Build (Pkg, Attr)), Default => "", Index => Index, Use_Extended => True); begin if Value = "" then declare Result : Unbounded_String; List : String_List_Access := Project.Attribute_Value (Attribute_Pkg_List'(Build (Pkg, Attr)), Index); begin if List /= null then for L in List'Range loop Append (Result, List (L).all); if L /= List'Last then Append (Result, " "); end if; end loop; Free (List); end if; Set_Return_Value (Data, To_String (Result)); end; else Set_Return_Value (Data, Value); end if; end; elsif Command = "scenario_variables" then declare Vars : constant Scenario_Variable_Array := Project_Tree.Scenario_Variables; begin for V in Vars'Range loop Set_Return_Value (Data, Value (Vars (V))); Set_Return_Value_Key (Data, External_Name (Vars (V))); end loop; end; elsif Command = "scenario_variables_cmd_line" then Name_Parameters (Data, Scenar_Var_Parameters); declare Prefix : constant String := Nth_Arg (Data, 1, ""); begin Set_Return_Value (Data, Scenario_Variables_Cmd_Line (Prefix)); end; elsif Command = "scenario_variables_values" then declare Tree : constant Project_Tree_Access := Project_Tree; Vars : constant Scenario_Variable_Array := Tree.Scenario_Variables; begin for V in Vars'Range loop declare Name : constant String := External_Name (Vars (V)); Values : String_List := Tree.Possible_Values_Of (Vars (V)); begin for Iter in Values'Range loop Set_Return_Value (Data, Values (Iter).all); Set_Return_Value_Key (Data, Name, True); end loop; Free (Values); end; end loop; end; end if; end Project_Command_Handler; --------------------- -- Project_Queries -- --------------------- procedure Project_Queries (Data : in out Callback_Data'Class; Command : String) is Project : constant Project_Type := Get_Data (Data, 1); Env : Project_Environment_Access; begin if Command = "get_executable_name" then declare Main : constant Virtual_File := GNATCOLL.Scripts.Files.Nth_Arg (Data, 2); begin Set_Return_Value (Data, Project.Executable_Name (Main.Full_Name.all)); end; elsif Command = "sources" then Name_Parameters (Data, Sources_Cmd_Parameters); declare Recursive : constant Boolean := Nth_Arg (Data, 2, False); Sources : File_Array_Access := Project.Source_Files (Recursive => Recursive); begin Set_Return_Value_As_List (Data); for S in Sources'Range loop Set_Return_Value (Data, GNATCOLL.Scripts.Files.Create_File (Get_Script (Data), Sources (S))); end loop; Unchecked_Free (Sources); end; elsif Command = "external_sources" then Env := Project.Get_Environment; if Env /= null then declare Sources : constant File_Array := Env.Predefined_Source_Files; begin Set_Return_Value_As_List (Data); for S in Sources'Range loop Set_Return_Value (Data, GNATCOLL.Scripts.Files.Create_File (Get_Script (Data), Sources (S))); end loop; end; end if; elsif Command = "languages" then Name_Parameters (Data, Languages_Cmd_Parameters); declare Langs : GNAT.Strings.String_List := Project.Languages (Recursive => Nth_Arg (Data, 2, False)); begin Set_Return_Value_As_List (Data); for L in Langs'Range loop Set_Return_Value (Data, To_Lower (Langs (L).all)); end loop; Free (Langs); end; elsif Command = "source_dirs" then Name_Parameters (Data, Source_Dirs_Cmd_Parameters); declare Recursive : constant Boolean := Nth_Arg (Data, 2, False); Dirs : constant File_Array := Project.Source_Dirs (Recursive => Recursive); begin Set_Return_Value_As_List (Data); for D in Dirs'Range loop -- ??? We should return the Virtual_File object instead Set_Return_Value (Data, Dirs (D).Full_Name); end loop; end; elsif Command = "object_dirs" then Name_Parameters (Data, Source_Dirs_Cmd_Parameters); declare Recursive : constant Boolean := Nth_Arg (Data, 2, False); Object : constant File_Array := Object_Path (Project, Recursive => Recursive, Including_Libraries => False); begin Set_Return_Value_As_List (Data); for J in Object'Range loop -- ??? Shouldn't we return a list of files instead ? Set_Return_Value (Data, Object (J).Full_Name); end loop; end; elsif Command = "exec_dir" then declare Exec_Dir : constant Virtual_File := Project.Executables_Directory; begin Set_Return_Value (Data, Exec_Dir.Full_Name); end; elsif Command = "target" then Data.Set_Return_Value (Project.Get_Target (Default_To_Host => False)); elsif Command = "artifacts_dir" then declare Dir : constant Virtual_File := Project.Artifacts_Dir; begin Set_Return_Value (Data, Dir.Full_Name); end; elsif Command = "save" then Data.Set_Return_Value (Project.Save); end if; end Project_Queries; ------------------ -- Project_Tree -- ------------------ function Project_Tree return GNATCOLL.Projects.Project_Tree_Access is begin if Retriever = null then return null; else return Retriever.Get_Project_Tree; end if; end Project_Tree; ----------------------- -- Register_Commands -- ----------------------- procedure Register_Commands (Repo : not null access Scripts_Repository_Record'Class; Value : not null access Project_Tree_Retriever'Class) is begin Retriever := Project_Tree_Retriever_Access (Value); Register_Command (Repo, Constructor_Method, Minimum_Args => 1, Maximum_Args => 1, Class => Get_Project_Class (Repo), Handler => Project_Command_Handler'Access); Register_Command (Repo, "root", Class => Get_Project_Class (Repo), Static_Method => True, Handler => Project_Command_Handler'Access); Register_Command (Repo, "name", Class => Get_Project_Class (Repo), Handler => Project_Command_Handler'Access); Register_Command (Repo, "file", Class => Get_Project_Class (Repo), Handler => Project_Command_Handler'Access); Register_Command (Repo, "ancestor_deps", Class => Get_Project_Class (Repo), Handler => Project_Command_Handler'Access); Register_Command (Repo, "dependencies", Class => Get_Project_Class (Repo), Minimum_Args => 0, Maximum_Args => 1, Handler => Project_Command_Handler'Access); Register_Command (Repo, "get_attribute_as_string", Minimum_Args => 1, Maximum_Args => 3, Class => Get_Project_Class (Repo), Handler => Project_Command_Handler'Access); Register_Command (Repo, "get_attribute_as_list", Minimum_Args => 1, Maximum_Args => 3, Class => Get_Project_Class (Repo), Handler => Project_Command_Handler'Access); Register_Command (Repo, "scenario_variables", Class => Get_Project_Class (Repo), Static_Method => True, Handler => Project_Command_Handler'Access); Register_Command (Repo, "scenario_variables_cmd_line", Minimum_Args => 0, Maximum_Args => 1, Class => Get_Project_Class (Repo), Static_Method => True, Handler => Project_Command_Handler'Access); Register_Command (Repo, "scenario_variables_values", Minimum_Args => 0, Maximum_Args => 0, Class => Get_Project_Class (Repo), Static_Method => True, Handler => Project_Command_Handler'Access); Register_Command (Repo, "sources", Maximum_Args => Sources_Cmd_Parameters'Length, Class => Get_Project_Class (Repo), Handler => Project_Queries'Access); Register_Command (Repo, "external_sources", Maximum_Args => 0, Class => Get_Project_Class (Repo), Handler => Project_Queries'Access); -- This one could be implemented as a property - but we are making this -- a method for homogeneity with "sources" from the perspective of the -- user. Register_Command (Repo, "source_dirs", Minimum_Args => Source_Dirs_Cmd_Parameters'Length - 1, Maximum_Args => Source_Dirs_Cmd_Parameters'Length, Class => Get_Project_Class (Repo), Handler => Project_Queries'Access); Register_Command (Repo, "get_executable_name", Params => (1 => Param ("main")), Class => Get_Project_Class (Repo), Handler => Project_Queries'Access); Register_Command (Repo, "languages", Minimum_Args => 0, Maximum_Args => 1, Class => Get_Project_Class (Repo), Handler => Project_Queries'Access); Register_Command (Repo, "object_dirs", Minimum_Args => Source_Dirs_Cmd_Parameters'Length - 1, Maximum_Args => Source_Dirs_Cmd_Parameters'Length, Class => Get_Project_Class (Repo), Handler => Project_Queries'Access); Register_Command (Repo, "exec_dir", Class => Get_Project_Class (Repo), Handler => Project_Queries'Access); Register_Property (Repo, "target", Class => Get_Project_Class (Repo), Getter => Project_Queries'Access); Register_Command (Repo, "artifacts_dir", Class => Get_Project_Class (Repo), Handler => Project_Queries'Access); Register_Command (Repo, "save", Class => Get_Project_Class (Repo), Handler => Project_Queries'Access); end Register_Commands; --------------------------------- -- Scenario_Variables_Cmd_Line -- --------------------------------- function Scenario_Variables_Cmd_Line (Prefix : String) return String is Scenario_Vars : constant Scenario_Variable_Array := Project_Tree.Scenario_Variables; Untyped_Vars : constant Untyped_Variable_Array := Project_Tree.Untyped_Variables; Res : Unbounded_String; begin for Var of Scenario_Vars loop Append (Res, Prefix & External_Name (Var) & "=" & Value (Var) & " "); end loop; for Var of Untyped_Vars loop Append (Res, Prefix & External_Name (Var) & "=" & Value (Var) & " "); end loop; return To_String (Res); end Scenario_Variables_Cmd_Line; -------------- -- Set_Data -- -------------- procedure Set_Data (Instance : Class_Instance; Project : Project_Type) is begin if not Is_Subclass (Instance, Project_Class_Name) then raise Invalid_Data; end if; Set_Data (Instance, Project_Class_Name, Project_Properties_Record'(Project => Project)); end Set_Data; end GNATCOLL.Scripts.Projects; gnatcoll-core-21.0.0/src/objlist_file.c0000644000175000017500000000374113661715457017611 0ustar nicolasnicolas/*---------------------------------------------------------------------------- -- G N A T C O L L -- -- -- -- Copyright (C) 2007-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -----------------------------------------------------------------------------*/ /* Dummy versions of __gnat_objlist_file_supported, __gnat_object_file_option and __gnat_using_gnu_linker (needed by mlib-utl.adb) */ unsigned char __gnat_objlist_file_supported = 0; const char *__gnat_object_file_option = ""; unsigned char __gnat_using_gnu_linker = 0; gnatcoll-core-21.0.0/src/gnatcoll-pools.ads0000644000175000017500000002153413661715457020426 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package implements resource pools. -- The resources are created once (the first time they are needed). The -- application can then get a temporary exclusive handle on a resource (i.e. -- if another part of the application is also requesting a resource, it will -- in fact retrieve another instance). When the resource is no longer used by -- the application, it is automatically released into the pool, and will be -- reused the next time the application requests a resource. -- -- A typical usage is when the resource creation is expensive, such as a pool -- of database connections. -- -- Each instantiation of this package provides one task-safe global pool. -- However, this pool can contains multiple sets of elements. Each sets has -- its own factory parameter. The subprograms will by default apply to the -- first such subset, since in most cases you will only need one such subset -- in your application. An example where multiple subsets might be needed is -- for instance when we have a pool of database connections. We would for -- instance one subset for each DBMS we want to connect to it. Each subset -- then contains a number of connections to that specific DBMS. -- Of course, this pattern could be implemented by having multiple -- instantiations of GNATCOLL.Pools, but this makes the API more complex and -- forces the duplication of the whole GNATCOLL.SQL.Session API. pragma Ada_2012; private with GNATCOLL.Refcount; generic type Element_Type is private; -- The elements that are pooled type Factory_Param is private; with function Factory (Param : Factory_Param) return Element_Type; -- Information needed to create new elements as needed. This is passed as -- is to the Factory function. type Resource_Set is (<>); -- Represents a set of elements within the pool. -- There can be multiple such sets in one pool. Each set is associated with -- its own factory parameter, but all other elements are compatible between -- the various sets (in particular, the resources are the same, so the rest -- of the application doesn't need to know which set this resource is -- from). -- Most times, a pool will need only one such subset, which is created by -- default. All subprograms below apply to this default set, unless -- otherwise specified. with procedure Free (Self : in out Element_Type) is null; -- Called when the [Self] is finally removed from the pool with procedure On_Release (Self : in out Element_Type) is null; -- Called when Self is released into the pool. -- The application has no more reference to that element, apart from the -- one in the pool. -- The result of Element.Element should not be freed yet, since it is -- returned to the pool (instead, override the formal [Free] parameter). -- But any other custom field from Element should be reset at that time. with procedure Free_Param (Data : in out Factory_Param) is null; -- Free Factory_Param. -- Called when the pool itself is freed. package GNATCOLL.Pools is Default_Set : constant Resource_Set; type Resource is tagged private; No_Resource : constant Resource; -- A resource retrieved from the pool. -- This is a smart pointer to an Element_Type. When your application has no -- more references to it, the Element_Type is released into the pool (not -- destroyed). -- The resource itself does its refcounting in a task-safe manner. function Element (Self : Resource) return access Element_Type; -- Get a copy of the element stored in the wrapper. The result should -- really only be used while you have a handle on Self, so that you are -- sure it has not been released into the pool, and thus reset. type Weak_Resource is private; Null_Weak_Resource : constant Weak_Resource; function Get_Weak (Self : Resource'Class) return Weak_Resource; procedure Get (Self : Weak_Resource; Res : out Resource); -- A resource with a weak-reference. -- Such a resource does not prevent the release into the pool when no other -- Resource exists. While the resource has not been released, you can get -- access to it through this Weak_Resource. One it has been released, the -- Weak_Resource will return No_Resource. -- This datatype can thus be stored in some long-lived data structure, if -- you do not want to prevent the release. For instance if you have a -- cache of some sort. function Was_Freed (Self : Weak_Resource) return Boolean; -- Whether the resource monitored by Self was released. procedure Set_Factory (Param : Factory_Param; Max_Elements : Positive; Set : Resource_Set := Default_Set); -- Configure the internal resource pool. This must be called before -- calling Get, and only once. procedure Get (Self : out Resource'Class; Set : Resource_Set := Default_Set); -- Return an available resource (or create a new one if the pool is not -- full yet and none is available). -- In a multitasking context, this blocks until a resource is actually -- available. -- The resource is automatically released when you no longer have a -- reference to the wrapper. procedure Free; -- Detach all resources from the pool. -- Any resource that is not in use elsewhere (i.e. retrieved by Get) will -- get freed (and the corresponding [Free] formal subprogram will be -- called). function Get_Refcount (Self : Resource) return Natural; -- Return the reference counting for self function Get_Factory_Param (Set : Resource_Set := Default_Set) return access Factory_Param; -- Returns the Factory_Param used for the set. -- Remember that the factory will not be called again for resources that -- have already been created, even if they have been released to the pool -- since then. -- This must only be called when you have called Set_Factory for the -- corresponding set. -- The returned value is shared among multiple threads, so you should only -- modify it from a protected region or before tasks are created. private type Pool_Resource is record Element : aliased Element_Type; Available : Boolean; -- Is the resource available ? end record; type Pool_Resource_Access is access all Pool_Resource; -- The data stored in the pool. -- These are not smart pointers, which are created on demand in Get. Default_Set : constant Resource_Set := Resource_Set'First; type Resource_Data is record In_Set : Pool_Resource_Access; Set : Resource_Set; end record; procedure Free (Self : in out Resource_Data); package Pointers is new GNATCOLL.Refcount.Shared_Pointers (Resource_Data, Free); -- The smart pointers returned to the application. When no longer -- referenced, the resource is released back into the pool. type Weak_Resource is record Ref : Pointers.Weak_Ref; end record; type Resource is new Pointers.Ref with null record; No_Resource : constant Resource := Resource'(Pointers.Null_Ref with null record); Null_Weak_Resource : constant Weak_Resource := (Ref => Pointers.Null_Weak_Ref); end GNATCOLL.Pools; gnatcoll-core-21.0.0/src/gnatcoll-strings_impl.ads0000644000175000017500000013362613661715457022012 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2017-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides the implementation for XString. -- Comparing with other string types -- ================================= -- Ada provides several kinds of strings: -- * A String -- is a fixed-width string, but very fast in general. Functions returning -- a String must do so on the secondary stack, which might be slow. -- * A Bounded_String -- has a known maximal size, but can represent any string smaller than this. -- It also doesn't do any memory allocation, and therefore is fast. You -- need one instance of the package for each size of bounded_string. This -- is the type to use if your coding standard restricts memory allocations. -- * An Unbounded_String -- is a string of any size, which automatically allocates more memory when -- needed. They are very flexible, but not very efficient. -- This package provides a fourth type of string, which tries to combine the -- advantages of all the above: -- * Unlimited size of the string, which grows as necessary -- More flexible than String and Bounded_String. -- * No memory allocation for small strings (for a certain definition of -- small, see the SSize parameter below). This means very fast handling of -- those small strings. -- Faster than Unbounded_String, both for small strings, as seen above, -- but also for large strings since one can use the Reserve procedure to -- preallocate enough space. -- * More extensive interface. -- For instance, you can use X(1) to get the first character of the string. -- The index always starts at 1, just like unbounded strings. -- * Supports various character types -- See the encodings section below. -- * Faster substrings -- When using copy-on-write, returning a substring does not require any -- copy of the data or memory allocation. This makes the operation much -- faster, in particular for operations that return lots of substrings, -- like Split. -- * Easy iteration -- It is possible to use a "for..of" loop to iterate on all valid indexes -- or on all characters, with a speed similar to what is done for a String. -- Task safety -- =========== -- Like Unbounded_String, a XString should not be accessed unprotected from -- several different tasks. We do not use locks for maximum efficiency. -- However, it is safe to pass a copy of a string to another thread, even when -- they share some data internally. The sharing is an implementation detail, -- and therefore properly encapsulated in this package. This package does not -- use locks internally, but atomic operations. -- So the following is invalid: -- -- Thread 1 | Thread 2 -- S.Set ("..."); | -- S.Append ("..."); | S.Append ("..."); -- invalid -- S.Append ("..."); | S.To_String; -- also invalid -- -- But the following is valid: -- -- Thread 1 | Thread 2 -- S.Set ("..."); | -- | lock; S2 := S; unlock; -- | -- S.Append ("..."); | S2.Append ("..."); -- at this point, they no longer share data, in fact -- Memory management -- ================= -- A major design decision for this package is that it does as little memory -- allocations as possible. Therefore for small strings, it does none. When -- the string grows, it ends up allocating a buffer, whose size will be -- increased when the string keeps growing. The growth strategy attempts a -- balance between speed and memory usage, so it will allocate more memory -- than strictly needed for the string, in case it eventually grows. See the -- Shrink subprogram if you need to restrict memory usage. -- When this package needs to return a copy of a string, or a substring, -- it has two possible strategies: -- * Either it tries to share the already allocated memory. As explained -- above, this is done in a thread-safe manner, and thus has a small -- performance penalty. On the other hand, it doesn't need to copy -- characters around, so it might be faster. -- As soon as you modify a string, the buffer can no longer be shared, -- and a copy is created (thus the term "copy on write"). -- * Or, if you disabled copy on write, it systematically allocates new -- memory when you do a copy or take a substring. This removes the -- need for atomic operations, and might be more efficient in heavily -- multi-threaded applications. -- We recommend doing actual performance measurement to decide which strategy -- to use. -- When you use this package, you could be seeing memory leaks when your -- program exits. This is because, like unbounded_string, it uses the pragma -- Finalize_Storage_Only which means that GNAT can skip the calls to free -- memory on exit, for performance reasons. It should not have memory leaks -- the rest of the time, though. -- Indexing -- ========= -- As opposed to what is done for standard Ada strings, all indexing always -- start at index 1. Even if you take a substring from indices 5 to 6, for -- instance, the resulting substring's first character is at index 1. -- This is both by design (for a lot of users, it is confusing to remember to -- use the proper indexes 'First and 'Last with standard strings), but also is -- needed because the internal buffer can be shared. For instance, when you -- take a substring as above, the internal buffer is shared (so we are really -- looking at characters 5 through 6 in this shared buffer). But as soon as -- you modify the substring, for instance by appending some data, this package -- needs to reallocate memory. In this case, it will move characters around, -- and we will be looking at actual characters 1 through 2 in the internal -- buffer. -- Always using "1" as the user-visible index for the first character ensures -- that any internal reallocation or move of data is transparent to the user. -- Unicode, character encodings,... -- ================================ -- When you need to manipulate characters outside of the ASCII character set -- (for instance accented letters, or Chinese symbols), things get more -- complicated. -- The unicode body assigned unique code points to all possible characters -- in use on Earth. These code points are 16 bits numbers. Ada provides -- various types to deal with code points: -- * A Character only represents code points 0 through 255. -- It is typically used to represent the Latin1 subset of unicode, -- i.e. western Europe characters. -- -- * A Wide_Character can represent mode Unicode code points, but -- requires twice as much memory to represent -- -- * A Wide_Wide_Character can represent all characters, but is even -- larger. -- There exist other character sets than Unicode, which usually predate it. -- They associate different character with codepoints. For instance, in the -- Latin1 charset, the code point 192 is "a grave"). But in latin5, it is a -- Russian letter. In Latin1, that Russian character cannot be represented. -- So to represent a character on the screen, we have to know its code point, -- but also its character set. The simplest, in terms of programming, it to -- always convert the input string from a known charset (say latin5) to -- unicode internally, so that we can always compare codepoints easily in the -- code, without the need for conversions all over the place. -- These code points (integers) need to be converted to strings so that we can -- display them, store them in files, input them,... Here, there also exists -- various ways to do that, called encodings: -- -- * Historical charset always have codepoints between 0 and 255 (and -- of course these are only a subset of all characters one might use -- in the world). So we simply use a series of bytes to represent them. -- In Ada, we would use a String for that purpose. But then, as stated -- above, this means that to compare two strings we have to know that -- they use the same charset. -- -- * Unicode defines a UTF-8 encoding. It can represent any codepoint, -- including greater than 255, but uses a variable number of bytes for -- code points. This is efficient in terms of storage (most of the -- time the characters only use a single byte), but costly in terms -- of manipulation since we have to make sure not to cut the string -- between two bytes of a multi-byte character. -- -- * Unicode also defines UTF-16, which has two variants, depending on -- whether the most significant byte goes first or not. Code points -- are represented on two bytes, though sometimes they will need a -- bit more for some rare codepoints. -- -- * Unicode finally defined UTF-32 (also with two variants), where -- codepoints are always represented as 4 bytes. -- As seen above, this is a very complex topic, and manipulating strings with -- different encodings, or comparing strings with different charsets becomes -- complex and costly in terms of performance. -- As a result, the choice taken in this package is to always store decoded -- strings (i.e. we don't use utf-8, utf-16 or anything else, but just store -- an array of code points). The Character_Type formal parameter can be used -- to chose the range of code points you want to be able to represent. -- Likewise, we always assume these are Unicode code points. -- So the workflow is the following: -- -- * Get an input string (from the user, a database,...), with a -- known charset and encoding. -- We call such a string a byte sequence since it cannot be -- interpreted correctly without information on the charset and -- encoding. -- -- * Decode this byte sequence into an XString -- -- * Manipulate the XString using any of the subprograms in this API. -- -- * If you need to output the string (to the user, into a file,...) -- encode it with an appropriate charset and encoding. private with Ada.Finalization; with Ada.Containers; with Ada.Strings; with GNATCOLL.Atomic; with System; package GNATCOLL.Strings_Impl is type String_Size is mod 2 ** 32; -- Internal size used for string sizes. This matches a Natural as used for -- standard Ada strings. Big_String_Size : constant := 2 * System.Word_Size + 2 * String_Size'Size; type Optimal_String_Size is mod (Big_String_Size / 8); for Optimal_String_Size'Size use Character'Size; -- Type used to instantiate GNATCOLL.Strings -- Ideal size is 19 bytes on 32 bits system or 23 on 64 bits systems), -- so that a small string (stored without memory allocation) takes the -- same size as a big string (not counting the size of the allocated -- memory). function Default_Growth (Current, Min_Size : String_Size) return String_Size; -- The default growth strategy. This decides how much memory we should -- allocate/reallocate for the internal buffer, based on the amount of -- memory we already use (Current), and the minimal number characters -- we need to store in the string. -- Current and Min_Size are given in 'characters' not in 'bytes', since -- a character could potentially take several bytes. generic type SSize is mod <>; -- Number of characters that can be stored in the XString object itself, -- without requiring memory allocations. -- We pass a type as opposed to an actual number of character because we -- need to apply representation clauses based on this type, which cannot -- be done if we have the number of characters. -- This type must be a range between 1 and 127. type Character_Type is (<>); -- The character type to use. You can use Character for compatibility -- with an Ada string, or Wide_Character for better Unicode support, -- or possibly other types. type Character_String is array (Positive range <>) of Character_Type; -- An array of Char_Type, i.e. a string as can be stored on the -- stack. Space : Character_Type := Character_Type'Val (Character'Pos (' ')); -- The space character with function To_Lower (Item : Character_Type) return Character_Type is <>; with function To_Upper (Item : Character_Type) return Character_Type is <>; -- character-specific functions. -- In general, you do not need to specify those if you have 'use'd -- the package Ada.Characters.Handling or Ada.Wide_Characters.Handling. Copy_On_Write : Boolean := GNATCOLL.Atomic.Is_Lock_Free; -- Whether we only duplicate strings when they are actually modified. -- The alternative is to duplicate them every time a xstring is copied -- into another. The latter might be faster in some cases (less -- contention in multithreading for instance). with function Growth_Strategy (Current, Min_Size : String_Size) return String_Size is Default_Growth; -- See the comment for Default_Growth package Strings is pragma Compile_Time_Error (Natural (SSize'Last) > 2 ** 7, "SSize too large"); subtype Char_Type is Character_Type; subtype Char_String is Character_String; Null_Char_String : constant Char_String := (1 .. 0 => <>); -- Local renamings, so that users of the package can use these types. type XString is tagged private with Constant_Indexing => Get, Variable_Indexing => Reference, Iterable => (First => First, Next => Next, Has_Element => Has_Element, Element => Get); pragma Preelaborable_Initialization (XString); Null_XString : constant XString; -- This Null_XString is always equal to an empty string. So you -- can use either -- if Str = "" then -- or if Str = Null_Xstring then type Unconstrained_Char_Array is array (1 .. Natural'Last) of aliased Char_Type; type Char_Array is access all Unconstrained_Char_Array; pragma Suppress_Initialization (Unconstrained_Char_Array); pragma No_Strict_Aliasing (Char_Array); for Char_Array'Storage_Size use 0; -- Type used to obtain a string access to a given address. -- Initialization is suppressed to handle pragma Normalize_Scalars. -- No variable of this type can be declared. It is only used via an -- access type (The storage size clause ensures we do not allocate -- variables of this type). -- It is the responsibility of the user to check the proper bounds. type XString_Array is array (Positive range <>) of XString; ---------------- -- Properties -- ---------------- function Length (Self : XString) return Natural; -- The number of characters in the string function Is_Empty (Self : XString) return Boolean is (Self.Length = 0); -- Whether the string is empty. function Get (Self : XString; Index : Positive) return Char_Type with Inline; -- Return the Index-th character of the string. -- The index always starts at 1. -- -- raises Ada.Strings.Index_Error if this is not a valid index. -- A simpler way to use this function is simply to use indexing: -- Self (Index) -- as done for a regular Ada string. type Character_Reference (Char : not null access Char_Type) is limited private with Implicit_Dereference => Char; -- A type through which we can modify a character of a string. -- It is made limited to make it harder to keep such a reference and -- pass it as parameter, for instance. -- Such a reference becomes invalid as soon as the contents of the -- string is modified, and could potentially reference freed memory. function Reference (Self : aliased in out XString; Index : Positive) return Character_Reference with Inline; -- Returns a reference to a specific character in the string. -- It is possible to change the contents of the string via this -- function. It is meant to be used implicitly as in: -- Self (Index) := 'A'; -- -- This makes Self unshareable, so that if you later do: -- S2 := Self; -- then S2 will have to make a copy of the string even when using -- copy-on-write. -------------------------- -- Iteration on indexes -- -------------------------- type Index_Range is record Low, High : Natural; end record with Iterable => (First => First, Next => Next, Has_Element => Has_Element, Element => Element); function First (Self : Index_Range) return Positive is (Self.Low); function Next (Ignored_Self : Index_Range; Index : Positive) return Positive is (Index + 1); function Has_Element (Self : Index_Range; Index : Positive) return Boolean is (Index <= Self.High); function Element (Ignored_Self : Index_Range; Index : Positive) return Positive is (Index); function Iterate (Self : XString) return Index_Range is ((Low => 1, High => Self.Length)); -- Provide an iterator to get all indexes of the string. -- This provides a convenient iterator: -- for Index of Self.Iterate loop -- C := Self (Index); -- end loop; -- This loop is about as fast as iterating directly on a -- String via a 'Range attribute. ----------------------------- -- Iteration on characters -- ----------------------------- function First (Self : XString) return Positive is (1); function Next (Self : XString; Index : Positive) return Positive is (Index + 1); function Has_Element (Self : XString; Index : Positive) return Boolean is (Index <= Self.Length); -- Standard iteration functions. -- Each iteration returns the next character in the string. -- -- Although this is better used as -- for C of Str loop -- null; -- end loop; -- -- See the Iterate function if you need to get the indexes instead ---------------------- -- Building strings -- ---------------------- -- No operator "&" is provided, for efficiency reasons. Such an -- operator would need to create temporary strings which then -- need to be freed almost immediately. Since this becomes a slow -- operation, this API does not provide it by default. procedure Set (Self : in out XString; Str : Char_String); -- Store a string in Self function To_XString (Str : Char_String) return XString; -- Same as creating a temporary function and Set-ing its value. -- This is less efficient that Set and results in more copies. procedure Append (Self : in out XString; Str : Char_String); procedure Append (Self : in out XString; Char : Char_Type); procedure Append (Self : in out XString; Str : XString); -- Append to the end of Self. function "*" (Count : Natural; Right : Char_Type) return XString; function "*" (Count : Natural; Right : Char_String) return XString; function "*" (Count : Natural; Right : XString) return XString; -- Build a new string that duplicates the Right parameter Count times procedure Reserve (Self : in out XString; Capacity : String_Size); -- Make sure Self has enough storage to contain a string of length -- Size. This doesn't impact the current value of Self, so if the -- current length is greater than Size, nothing is done. -- More memory could be allocated, for performance reasons. procedure Shrink (Self : in out XString); -- Shrinks the memory used by Self to the minimum needed. This will -- likely require some memory allocation and copying the characters. procedure Swap (Self, Str : in out XString); -- Swap the contents of the two strings. -- This is more efficient than using an intermediate variable, and -- is often useful in various algorithms. ------------------------ -- Justifying strings -- ------------------------ procedure Center (Self : in out XString; Width : Positive; Pad : Char_Type := Space); function Center (Self : XString; Width : Positive; Pad : Char_Type := Space) return XString; -- Center Self, and surround it with Pad characters, such that the -- total width is Width. -- If Self is longer than Width, it is unmodified (so the result -- could be longer than Width, use Head if you want to make sure -- this isn't the case). -- The function is not efficient since it needs to allocate memory -- and copy the characters. procedure Left_Justify (Self : in out XString; Width : Positive; Pad : Char_Type := Space); function Left_Justify (Self : XString; Width : Positive; Pad : Char_Type := Space) return XString; -- Add Pad characters at the end of Self, so that the resulting -- string is of length Width. -- If Self is longer than Width, it is returned as is. -- The function is not efficient since it needs to allocate memory -- and copy the characters. procedure Right_Justify (Self : in out XString; Width : Positive; Pad : Char_Type := Space); function Right_Justify (Self : XString; Width : Positive; Pad : Char_Type := Space) return XString; -- Add Pad characters at the beginning of Self, so that the resulting -- string is of length Width. -- If Self is longer than Width, it is returned as is. -- The function is not efficient since it needs to allocate memory -- and copy the characters. --------------- -- Comparing -- --------------- function "=" (Left : XString; Right : Char_String) return Boolean; function "=" (Left : XString; Right : XString) return Boolean; function "=" (Left : Char_String; Right : XString) return Boolean is (Right = Left); function "<" (Left : XString; Right : Char_String) return Boolean; function "<" (Left : Char_String; Right : XString) return Boolean; function "<" (Left : XString; Right : XString) return Boolean; function "<=" (Left : XString; Right : Char_String) return Boolean; function "<=" (Left : Char_String; Right : XString) return Boolean; function "<=" (Left : XString; Right : XString) return Boolean; function ">" (Left : XString; Right : Char_String) return Boolean is (not (Left <= Right)); function ">" (Left : Char_String; Right : XString) return Boolean is (not (Left <= Right)); function ">" (Left : XString; Right : XString) return Boolean is (not (Left <= Right)); function ">=" (Left : XString; Right : Char_String) return Boolean is (not (Left < Right)); function ">=" (Left : Char_String; Right : XString) return Boolean is (not (Left < Right)); function ">=" (Left : XString; Right : XString) return Boolean is (not (Left < Right)); -- Compare strings subtype Compare_Result is Integer range -1 .. 1; function Compare (Left : XString; Right : Char_String) return Compare_Result; function Compare (Left : XString; Right : XString) return Compare_Result with Inline; function Compare (Left : Char_String; Right : XString) return Compare_Result is (-Compare (Right, Left)); -- Compare two strings. -- If they are equal, returns 0. -- If Left is before Right in lexicographical order, return -1. -- If Left is after Right in lexicographical order, return 1. -- The standard operators above are not defined in terms of Compare -- because the compiler is sometimes able to generate more efficient -- code for them. function Compare_Case_Insensitive (Left : XString; Right : Char_String) return Compare_Result; function Compare_Case_Insensitive (Left : XString; Right : XString) return Compare_Result with Inline; function Compare_Case_Insensitive (Left : Char_String; Right : XString) return Compare_Result is (-Compare_Case_Insensitive (Right, Left)); -- Same as above, but ignore casing differences function Equal_Case_Insensitive (Left : XString; Right : Char_String) return Boolean is (Compare_Case_Insensitive (Left, Right) = 0); function Equal_Case_Insensitive (Left : XString; Right : XString) return Boolean is (Compare_Case_Insensitive (Left, Right) = 0); function Equal_Case_Insensitive (Left : Char_String; Right : XString) return Boolean is (Compare_Case_Insensitive (Left, Right) = 0); function Less_Case_Insensitive (Left : XString; Right : Char_String) return Boolean is (Compare_Case_Insensitive (Left, Right) = -1); function Less_Case_Insensitive (Left : XString; Right : XString) return Boolean is (Compare_Case_Insensitive (Left, Right) = -1); function Less_Case_Insensitive (Left : Char_String; Right : XString) return Boolean is (Compare_Case_Insensitive (Left, Right) = -1); ---------------- -- Converting -- ---------------- procedure Get_String (Self : XString; S : out Char_Array; L : out Natural) with Inline; -- Returns a pointer to the internal string data. -- Do not modify the characters in this string, since it could be -- shared among multiple strings. -- S is only valid as long as Self is not accessed or modified. procedure Access_String (Self : XString; Process : not null access procedure (S : Char_String)); -- Access the string contained in Self. -- While Process is running, Self itself will not be destroyed, even -- if Process should access Self and modify it. -- -- This might easier to use than Get_String, and is more efficient -- than To_String. function To_String (Self : XString) return Char_String; -- This functions returns the internal string. -- As much as possible, you should use Get_String instead, which is -- much more efficient. This function requires returning data whose -- size is not known statically to the compiler, thus requires using -- the secondary stack and copying the string. This can have significant -- performance impact when the string is big. ------------- -- Hashing -- ------------- function Hash (Self : XString) return Ada.Containers.Hash_Type; -- Return a hash value suitable for the standard containers map. -- This is not a cryptographic hash. function Hash_Case_Insensitive (Self : XString) return Ada.Containers.Hash_Type; -- Same as above, but ignore casing ------------ -- Casing -- ------------ procedure To_Upper (Self : in out XString); function To_Upper (Self : XString) return XString; -- Convert all characters of Self to upper case, using the formal -- parameter To_Upper. procedure To_Lower (Self : in out XString); function To_Lower (Self : XString) return XString; -- Convert all characters of Self to lower case, using the formal -- parameter To_Lower. procedure Capitalize (Self : in out XString); -- Make sure the first letter of Self is upper cased. -- All other characters are lower cased. procedure Title (Self : in out XString); -- The first letter and all letters after a space are upper cased, -- and all other characters are lower cased. function Is_Upper (Self : XString) return Boolean; function Is_Lower (Self : XString) return Boolean; -- True if all characters in Self are upper or lower cased ------------- -- Testing -- ------------- function Starts_With (Self : XString; Prefix : Char_String) return Boolean; function Starts_With (Self : XString; Prefix : XString) return Boolean; -- Whether Self starts with the specific prefix. function Ends_With (Self : XString; Suffix : Char_String) return Boolean; function Ends_With (Self : XString; Suffix : XString) return Boolean; -- Whether Self ends with the specific suffix. --------------- -- Searching -- --------------- -- These functions do not use advanced algorithms like Boyer-Moore, -- so do not take take advantage of the pattern to optimize the -- search. -- See also GNATCOLL.Boyer_Moore for more advanced algorithms function Count (Self : XString; Char : Char_Type; Low : Positive := 1; High : Natural := Natural'Last) return Natural; function Count (Self : XString; Str : Char_String; Low : Positive := 1; High : Natural := Natural'Last) return Natural; -- Return the number of non-overlapping occurrences of Char or Str. -- If Str is the empty string, returns Natural'Last (infinite). -- The search is done in the substring Low..High (by default the -- whole string). -- Index_Error is raised if Low is not a valid index (unless Self -- is the empty string). function Find (Self : XString; Char : Char_Type; Low : Positive := 1; High : Natural := Natural'Last) return Natural; function Find (Self : XString; Str : Char_String; Low : Positive := 1; High : Natural := Natural'Last) return Natural; function Right_Find (Self : XString; Char : Char_Type; Low : Positive := 1; High : Natural := Natural'Last) return Natural; function Right_Find (Self : XString; Str : Char_String; Low : Positive := 1; High : Natural := Natural'Last) return Natural; -- Return the index of the first occurrence of Char or Str, -- in the substring Self(Low..High). -- Index_Error is raised if Low is not a valid index (unless Self -- is the empty string). -- -- The Right_Find functions start searching from the right of -- Self. -- -- 0 is returned when no match was found or Str is the empty string. ---------------- -- Substrings -- ---------------- -- The following subprograms return a substring of Self, based on -- various criteria. -- -- When using copy-on-write, these subprograms will share the storage -- of Self, and thus will not require new memory allocation. This -- makes them fast. -- When not using copy-on-write, however, they require copy of the -- characters and memory allocations. The functions are even more -- expensive, since they require additional copies, so we recommend -- using the procedures instead. -- -- All returned substrings always start at index 1, even if you took -- a slice from another index on. procedure Slice (Self : in out XString; Low : Positive; High : Natural); function Slice (Self : XString; Low : Positive; High : Natural) return XString; procedure Slice (Self : XString; Low : Positive; High : Natural; Into : in out XString); -- Return a substring of Self. -- The first character of Self is always at index 1, so this function -- returns a slice from the Low-th character of Self to the High-th -- character of Self. -- -- raises Ada.Strings.Index_Error if any of the indexes is invalid. procedure Trim (Self : in out XString; Side : Ada.Strings.Trim_End := Ada.Strings.Both; Chars : Char_Type := Space); function Trim (Self : XString; Side : Ada.Strings.Trim_End := Ada.Strings.Both; Chars : Char_Type := Space) return XString; -- Remove characters on either end of the string. -- All characters equal to Chars are removed from either ends. function Head (Self : XString; Count : Natural) return XString; -- Return the first Count characters of Self. -- If Self is smaller, it is returned as is. function Tail (Self : XString; Count : Natural) return XString; -- Return the last Count characters of Self. -- If Self is smaller, it is returned as is. function Split (Self : XString; Sep : Char_Type; Max_Split : Positive := Positive'Last; Omit_Empty : Boolean := False) return XString_Array; procedure Split (Self : XString; Sep : Char_Type; Omit_Empty : Boolean := False; Into : out XString_Array; Last : out Natural); function Split (Self : XString; Sep : Char_String; Max_Split : Positive := Positive'Last; Omit_Empty : Boolean := False) return XString_Array; procedure Split (Self : XString; Sep : Char_String; Omit_Empty : Boolean := False; Into : out XString_Array; Last : out Natural); -- Split self into chunks, on every occurrence of Sep. -- -- The procedure is faster since it does fewer copies of the strings, -- in particular when not using Copy-On-Write. Only elements from -- Into'First .. Last have been set or modified. Others are left -- untouched. -- -- If Max_Split is specified, at most that many substrings are -- returned, and the last one extends till the end of Self. -- The procedure uses the size of Into has the maximum number of -- splits that are allowed. -- Specifying a Max_Split is more efficient, since otherwise these -- subprograms need to count the number of times splitting is -- necessary. -- -- If Omit_Empty is true, then none of the returned substring will -- be the empty string. -- -- For instance, if Self = "1,,2,3,,4", then: -- * Sep=',' => ["1", "2", "", "3", "", "4"] -- * Sep=',' and Omit_Empty=True => ["1", "2", "3", "4"] -- * Sep=',' and Max_Split=3 => ["1", "2", "3,,4"] -- As another example, if you need to split on consecutive whitespaces -- you can use Omit_Empty=True and Sep=' ', for instance: -- " 2 3 4" => ["2", "3", "4"] -- -- Splitting an empty string will return an empty array. -- Splitting on an empty Sep has the same effect. function Right_Split (Self : XString; Sep : Char_Type; Max_Split : Positive := Positive'Last; Omit_Empty : Boolean := False) return XString_Array; procedure Right_Split (Self : XString; Sep : Char_Type; Omit_Empty : Boolean := False; Into : out XString_Array; Last : out Natural); function Right_Split (Self : XString; Sep : Char_String; Max_Split : Positive := Positive'Last; Omit_Empty : Boolean := False) return XString_Array; procedure Right_Split (Self : XString; Sep : Char_String; Omit_Empty : Boolean := False; Into : out XString_Array; Last : out Natural); -- Same as Split, but starting from the right. -- The substrings are returned in the reverse order, from right to -- left in Self. procedure Set_As_Join (Self : out XString; Sep : Char_String; Items : XString_Array; Prefix : Char_String := Null_Char_String; Suffix : Char_String := Null_Char_String); function Join (Sep : Char_String; Items : XString_Array; Prefix : Char_String := Null_Char_String; Suffix : Char_String := Null_Char_String) return XString; function Join (Sep : XString; Items : XString_Array; Prefix : Char_String := Null_Char_String; Suffix : Char_String := Null_Char_String) return XString; procedure Set_As_Join (Self : out XString; Sep : Char_Type; Items : XString_Array; Prefix : Char_String := Null_Char_String; Suffix : Char_String := Null_Char_String); function Join (Sep : Char_Type; Items : XString_Array; Prefix : Char_String := Null_Char_String; Suffix : Char_String := Null_Char_String) return XString; -- Return a string that contains all elements from Items, separated -- by Self. -- Prefix is automatically added before the string, -- while Suffix is added after the string. Using them might save some -- extra memory allocation or copying. -- The function versions are less efficient (more so when not using -- copy-on-write). --------------- -- Modifying -- --------------- procedure Replace (Self : in out XString; Index : Positive; Char : Char_Type); -- Replace a specific character in the string. -- Index_Error is raised if the index is invalid. procedure Replace (Self : in out XString; Low : Positive; High : Natural; By : Char_String); procedure Replace_Slice (Self : in out XString; Low : Positive; High : Natural; By : XString) with Inline; -- Replace the substring Low..High with By. -- Low must be a valid index (but High might be larger than the -- string's length). -- If High < Low, this is the equivalent of inserting the new -- string at position Low. procedure Insert (Self : in out XString; Before : Positive; New_Item : Char_String) with Inline; procedure Insert (Self : in out XString; Before : Positive; New_Item : XString) with Inline; -- Insert the new item at the given position in Self. procedure Overwrite (Self : in out XString; Position : Positive; New_Item : Char_String) with Inline; procedure Overwrite (Self : in out XString; Position : Positive; New_Item : XString) with Inline; -- Replace the substring at the given Position with the new -- item. If Self is longer, characters after are preserved. procedure Delete (Self : in out XString; Low : Positive; High : Natural) with Inline; -- Delete the substring Low..High. -- Both indexes must be valid. procedure Clear (Self : in out XString); -- Reset the contents of Self, and frees all allocated memory. -- You do not need to call this procedure in general, since memory -- is handled automatically. For instance, when Self goes out of -- scope, memory is freed. -- In general, it is more efficient to call Set on the string without -- calling Clear first, since GNATCOLL will be able to reuse already -- allocated memory in such a case. private Max_Small_Length : constant String_Size := String_Size (SSize'Last); -- Number of bytes in the small_string buffer, as decided by the user. type Character_Reference (Char : not null access Char_Type) is null record; type Big_String_Data (Copy_On_Write : Boolean) is limited record case Copy_On_Write is when False => Bytes1 : Unconstrained_Char_Array; when True => Refcount : aliased GNATCOLL.Atomic.Atomic_Counter; Bytes2 : Unconstrained_Char_Array; end case; end record with Unchecked_Union; type Big_String_Data_Access is access all Big_String_Data; pragma Suppress_Initialization (Big_String_Data); pragma No_Strict_Aliasing (Big_String_Data_Access); -- Unsafe: this is the data used by big strings to store the actual -- byte sequence. When we use refcounting, we need to have an explicit -- refcount, which is not needed otherwise. type Small_String is record Is_Big : Boolean; Size : SSize; Data : Char_String (1 .. Natural (Max_Small_Length)); end record; for Small_String use record Is_Big at 0 range 0 .. 0; Size at 0 range 1 .. 7; end record; pragma Suppress_Initialization (Small_String); -- Hard-code the fact that we can represent the small size on 7 bits -- (the pragma Compile_Time_Error ensures this is the case). Would be -- nice if we could use "15" of "7" for larger small string, but we -- would need static constants for this, and generic formal are not -- (so that compilers can implement shared generics). subtype Half_Capacity_Size is String_Size range 0 .. 2 ** 31 - 1; type Big_String is record Is_Big : Boolean; Half_Capacity : Half_Capacity_Size; Size : String_Size; Data : aliased Big_String_Data_Access; -- This field must be aligned on multiple of word_size, so can't -- be last. First : Positive; -- Index of the first character in data. -- This is used to share the data between substrings. When we -- do not use copy-on-write, part of the buffer might becomes -- useless but this is faster than reallocating and copying. -- On 64-bits platforms, we have 32 bits unused here. end record; for Big_String use record Is_Big at 0 range 0 .. 0; Half_Capacity at 0 range 1 .. 31; Size at 4 range 0 .. 31; Data at 8 range 0 .. System.Word_Size - 1; First at 8 + System.Word_Size / 8 range 0 .. 31; end record; for Big_String'Size use Big_String_Size; pragma Suppress_Initialization (Big_String); -- Capacity is always an even number, and we store half of it, so that -- it leaves one bit for the flag. type String_Data (Is_Big : Boolean := False) is record case Is_Big is when False => Small : Small_String; when True => Big : Big_String; end case; end record with Unchecked_Union; type XString is new Ada.Finalization.Controlled with record Data : String_Data := (Is_Big => False, Small => <>); end record; overriding procedure Adjust (Self : in out XString); overriding procedure Finalize (Self : in out XString); pragma Finalize_Storage_Only (XString); -- Finalization is only required for freeing storage pragma Stream_Convert (XString, To_XString, To_String); -- provide stream routines without dragging in Ada.Streams Null_XString : constant XString := (Ada.Finalization.Controlled with Data => (Is_Big => False, Small => <>)); end Strings; -- Unbounded strings have: -- Index_Non_Blank Find_Token Translate -- Ada.Strings.UTF_Encoding -- Can we reorganize to share code between various instances that -- use the same Char_Type and Char_String ? -- C++ has: -- rfind find_first_of find_last_of -- find_first_not_of find_last_not_of -- Python adds: -- "in" isalpha isprintable -- format isdecimal isspace partition -- splitlines expandtabs isdigit istitle -- zfill isidentifier isalnum swapcase -- casefold isnumeric end GNATCOLL.Strings_Impl; gnatcoll-core-21.0.0/src/gnatcoll-traces.ads0000644000175000017500000012125613661715457020555 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2001-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- Logging framework -- Usage in the code -- ================= -- Here is an example of code: -- -- with GNATCOLL.Traces; use GNATCOLL.Traces; -- -- procedure Main is -- Me : constant Trace_Handle := Create ("NAME"); -- begin -- Parse_Config_File (".gnatdebug"); -- mandatory -- Trace (Me, "Message 1"); -- end Main; -- -- You would then provide an additional .gnatdebug file in the current -- directory, see below for its format. -- Configuration -- ============== -- -- The format of the configuration file is the following: -- * activating or deactivating a specific module: -- -- MODULE_NAME=yes -- MODULE_NAME -- same as "=yes" -- MODULE_NAME=no -- default, unless you used "+" -- MODULE_NAME=yes :option1:option2 -- -- where the options are generally given as key=value. The valid -- options are: -- -- fg=color # where color is red|black|green|yellow|blue| -- # magenta|cyan|gray -- bg=color # Same colors as for fg -- style=style # where style is bright|dim|normal -- -- These options configure the default color of the messages in -- this stream. The colors are ignored unless you also enable -- DEBUG.COLORS (see below). The exact color that is output depends -- on the configuration of your terminal (see GNATCOLL.Terminal for -- more information). -- * redirecting a specific module to a file -- -- MODULE_NAME=yes >filename -- MODULE_NAME=yes >&stream -- MODULE_NAME=yes :option1:option2 >filename -- * Activate all modules, except those with an explicit "=no" line, -- and those that are created with "Create (..., Default => Off)" in -- the code. This command does not apply to decorators like -- DEBUG.COLORS (see below). -- -- + -- * redirecting all modules to a different stream: -- - to a file: -- -- >filename -- -- If filename is a relative path, it is relative to the location of -- the configuration file. $$ is automatically replaced by the -- process number. $D is automatically replaced by the current date. -- $T is automatically replaced by the current date and time. -- You can use >>filename instead if you want to append to the file. -- -- - to standard output -- -- >&1 -- -- - to standard error -- -- >&2 -- -- - to a user-defined stream (see gnat-traces-syslog.ads): -- -- >&stream -- -- In all the cases above, the name of the stream can be followed by -- one or more options, for instance: -- >filename:buffer_size=0 -- >&1:colors=on -- >&stream:option1:option2 -- -- The list of options is given below. They do not necessarily apply -- to all streams (for instance controlling the buffer size is not -- supported for standard output or standard error, and syslog does -- not support colors). -- -- * "buffer_size": the size of the buffer. The logs are synchronized -- with the disk when this buffer is full. -- Setting this to 0 means that synchronization appears after every -- write operation into log file. Value 1 means synchronization after -- every output line, which is the same as buffer_size=0 in the -- current implementation. Bigger buffer_size value improves the trace -- performance but can result in loss of information on application -- crash. The default buffer_size value is 1. -- -- * "colors": whether to allow colors on this stream. -- This combines with the DEBUG.COLORS settings. -- Setting this to "on" or "true" forces color output, to -- "off" or "false" disables colors, and "auto" will try and -- auto-detect whether the terminal supports colors. -- For Windows users, note that colors are only supported via -- the use of ANSI sequences (see gnatcoll-terminal.ads) -- * comments -- -- comment -- Wildcards -- ========= -- It is also possible to substitute a module name with a '*', to configure -- a whole set of modules with a single line. For instance: -- -- * *.EXCEPTIONS=yes >&stdout -- will always display a stream whose name ends with ".EXCEPTIONS" to -- stdout. -- -- * MODULE_NAME.*=no -- Disables all streams starting with "MODULE_NAME" (including -- MODULE_NAME itself). The star can only be used to substitute the -- whole first or last name. If the configuration file also contains -- a line like "MODULE_NAME.FOO" anywhere (before or after), then this -- specific stream is not disabled. -- -- * *.ERROR=yes :fg=red -- Enable all modules ending with "ERROR", and display their messages -- in red by default. -- Decorators -- ========== -- All messages are output with decorators, which can add extra information -- like timestamp, colors, message count,... Those decorators are -- configured like modules (see above), but have reserved names. It is -- possible to create your own decorators, but all of the predefined -- decorators start with "DEBUG.". Here is the extensive list: -- "DEBUG.ABSOLUTE_TIME" -- If this handle is activated, then the absolute time will be added to the -- output, if the stream supports it (syslog does not) -- "DEBUG.MICRO_TIME" -- If this handle is activated, the absolute time will be displayed using -- micro-seconds resolution, instead of just seconds. -- "DEBUG.ABSOLUTE_DATE" -- If this handle is activated, then the absolute date will be added to the -- output, if the stream supports it (syslog does not) -- "DEBUG.ELAPSED_TIME" -- If this handle is activated, then the elapsed time since the last -- call to Trace for this handler will be displayed. -- "DEBUG.STACK_TRACE" -- If this handle is activated, then the stack trace will be displayed. -- "DEBUG.LOCATION" -- If this is activated, then the location of the call to Trace is -- displayed. Note that, contrary to DEBUG.STACK_TRACE, this works on -- all targets, and even if the executable wasn't compiled with debug -- information. -- "DEBUG.COLORS" -- When this config is enabled, then other decorators (like the name of -- the handle, current time, count,...) will be displayed in color when -- the stream supports them (see also the "colors" option when you -- declare the streams, at the top of this package). -- "DEBUG.ENCLOSING_ENTITY" -- If this handle is activated, the name of the enclosing entity at the -- location of the call to Trace will be displayed. -- "DEBUG.COUNT" -- If this handle is active, two counters are associated with each output -- trace: one of them is unique for the handle, the other is unique in the -- whole application life. These can for instance be used to set -- conditional breakpoints for a specific trace (break on traces.Log or -- traces.Trace, and check the value of Handle.Count -- "DEBUG.MEMORY" -- This decorator will show the size of resident memory for the -- application, as well as the peek size. This takes into account memory -- allocated from any language, C, Ada,.. and is queries from the -- operating system). -- It also shows a ">" or "<" to indicate whether memory use increased or -- not. -- "DEBUG.ADA_MEMORY" -- This is similar to DEBUG.MEMORY, but only displays memory allocated -- from Ada (provided you have setup GNATCOLL.Memory to become the default -- allocator for your application). -- "DEBUG.FINALIZE_TRACES" (default: active) -- If deactivated, the trace handles will never be freed when the program -- is finalized by the compiler. This is mostly for debugging purposes. -- "DEBUG.SPLIT_LINES" (default: true) -- Whether long messages should be split at each ASCII.LF character. When -- we do this, the trace handle name and decorators are replicated at the -- beginning of each follow-up line. This results in a slow down. -- Example -- ======= -- Here is a short example of configuration file: -- + -- by default, show all -- >&2 -- defines the default stream -- DEBUG.COLORS=yes -- enable colors -- PKG1=no -- do not show -- PKG2=yes -- to the default stream, i.e. stderr -- PKG3=yes >file -- to the file "file" in current directory -- PKG4=yes >&syslog -- to syslog, see gnat-traces-syslog.ads with GNAT.Source_Info; with GNAT.Strings; with Ada.Calendar; with Ada.Exceptions; with Ada.Characters.Handling; use Ada.Characters.Handling; private with Ada.Finalization; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.Atomic; use GNATCOLL.Atomic; with GNATCOLL.Strings_Impl; with GNATCOLL.Terminal; package GNATCOLL.Traces is Config_File_Environment : constant String := "ADA_DEBUG_FILE"; Default_Config_File : constant Filesystem_String := ".gnatdebug"; -- Name of the default configuration file. This file is looked for first in -- the current directory, then in the user's home directory. If no file is -- found, then no handle will be activated. The name of this file can be -- overridden by the environment variable Config_File_Environment, which -- should be an absolute name (or relative to the current directory). If -- this variable is set, the standard file is never searched. Debug_Mode : constant Boolean := True; -- Set the global activation status for the debug traces. If this is set to -- False and the subprograms below are inlined, then no code will be -- generated to support debug traces. Otherwise, if Debug_Mode is True, -- then the debug traces can be activated selectively for each module. type On_Exception_Mode is (Propagate, Ignore, Deactivate); -- Behavior when an exception is raised while writing to the log stream e.g -- because of NFS error when writing to a file. -- Propagate: the exception is propagated -- Ignore: the exception is silently ignored -- Deactivate: when an exception is raised when manipulating a handle -- deactivate it; no logging will happen on this handle -- anymore. procedure Parse_Config_File (Filename : Virtual_File; Default : Virtual_File := No_File; On_Exception : On_Exception_Mode := Propagate; Force_Activation : Boolean := True); -- Initializes this package, and parse the configuration file. The -- algorithm is the following: -- - If filename is specified and exists on the disk, parse this file -- - Else test the file described in Config_File_Environment -- - If not found, search in the current directory for a file -- Default_Config_File -- - If not found, search in the user's home directory for a file -- Default_Config_File -- - If still not found, parses Default -- On_Exception is used to define the behavior should something unexpected -- prevent the log stream to be written. -- -- If the file is found on the disk, or Force_Activation is True: -- This procedure will set the default stream. At this -- stage, most loggers will start outputting information. If you do not -- call Parse_Config_File, then most loggers will have no associated -- stream and therefore will not output anything. An alternative is to -- simply call Parse_Config below. procedure Parse_Config_File (Filename : String := ""; Default : String := ""; On_Exception : On_Exception_Mode := Propagate; Force_Activation : Boolean := True); -- Same as above, using regular strings for file names. procedure Parse_Config (Config : String; On_Exception : On_Exception_Mode := Propagate; Force_Activation : Boolean := True; Relative_Path_To : GNATCOLL.VFS.Virtual_File := GNATCOLL.VFS.Get_Current_Dir); -- Similar to the above, but the configuration is read from a string. -- This might be convenient when you distribute your application since you -- do not have to provide a default config file. -- You can call this procedure multiple times. -- -- Relative_Path_To is used to resolve relative path names in the -- configuration. -- -- It is still recommended to parse Parse_Config_File afterwards so that -- you can override the configuration without having to recompile your -- application. type Output_Proc is access procedure (Str : String); procedure Show_Configuration (Output : Output_Proc); -- Output on Output the current configuration for all traces. The resulting -- file is a valid configuration file, which can be reused in later runs. procedure Finalize; -- Free all the registered handles. This is not strictly needed, but is -- specially useful when testing memory leaks in your application. This -- also ensures that output streams are correctly closed. ------------- -- Loggers -- ------------- type Trace_Handle_Record is tagged limited private; type Trace_Handle is access all Trace_Handle_Record'Class; subtype Logger is Trace_Handle; -- alternative name -- A handle for a trace stream. -- One such handle should be created for each module/unit/package where it -- is relevant. They are associated with a specific name and output stream, -- and can be activated through a configuration file. If two or more -- packages create streams with the same name, they will share their -- attributes. type Handle_Factory is access function return Logger; type Default_Activation_Status is (From_Config, On, Off); function Create (Unit_Name : String; Default : Default_Activation_Status := From_Config; Stream : String := ""; Factory : Handle_Factory := null; Finalize : Boolean := True) return Logger; -- Create a new handle -- Unit_Name is upper-cased, and looked-for in the configuration file to -- check whether traces should be emitted for that module. Calling this -- function several times with the same Unit_Name will always return the -- same handle. -- -- If Default is not From_Config, this forces an explicit activation -- status for that handle. To change it, the user must explicitly have -- a line for this handle in the config file, and this handle is not -- impacted by the use of "+" in this config file. -- -- Stream indicates which stream the application is sent to. This has the -- same format as in the configuration file: -- - if left to the empty string, this is the default stream specified -- in the configuration file on the line starting with ">". -- - otherwise, the string is similar to what can be specified in the -- configuration file after ">", ie one of "filename", "&2" (stderr), -- "&1" (stdout) or any of the registered streams ("&syslog" for -- instance, see gnat-traces-syslog.ads) -- If no such stream is found, defaults on the default stream declared in -- the config file. -- -- If the handle has not been created yet in some other part of the code, -- a new one will be allocated. Factory can be used in this case to do the -- actual allocation, so that you can return your own Trace_Handle_Record, -- when you need to override Trace. The factory is only called once. -- -- If Finalize is True, the handle will be freed when Finalize is called, -- otherwise it won't be. The only reason to set this to False is so that -- the handle still exists when the application itself is being finalized -- by the compiler, so that you can have logs till the last minute. -- See also the "DEBUG.FINALIZE_TRACES" configuration. function Exists (Unit_Name : String) return Boolean; -- Return True if the handle has been created in some other part of the -- code. If Unit_Name starts and/or finishes by '*' then this function -- will check if a corresponding wildcard handle exists. function Unit_Name (Handle : not null access Trace_Handle_Record'Class) return String; -- Return the unit name (upper-cased) for this handle. This can be used for -- instance in generic packages to specialize the handle for a specific -- instance. -- -- Recommended use with generics: -- Since generics might be used in various, independent modules, the -- recommended use is to have one more generic parameter for the logger -- Internally, it is then possible to specialize this stream (see -- the subprogram Unit_Name): -- generic -- Self_Debug : Logger := Create ("My_Generic"); -- package My_Generic is -- Me : Logger := Create ("Generic" & Unit_Name (Self_Debug)); -- ... Red_Fg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Red, Bg => Terminal.Unchanged)); Green_Fg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Green, Bg => Terminal.Unchanged)); Brown_Fg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Yellow, Bg => Terminal.Unchanged)); Blue_Fg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Blue, Bg => Terminal.Unchanged)); Purple_Fg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Magenta, Bg => Terminal.Unchanged)); Cyan_Fg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Unchanged, Fg => Terminal.Cyan, Bg => Terminal.Unchanged)); Grey_Fg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Grey, Bg => Terminal.Unchanged)); Default_Fg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Unchanged, Fg => Terminal.Reset, Bg => Terminal.Unchanged)); Red_Bg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Unchanged, Bg => Terminal.Red)); Green_Bg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Unchanged, Bg => Terminal.Green)); Brown_Bg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Unchanged, Bg => Terminal.Yellow)); Blue_Bg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Unchanged, Bg => Terminal.Blue)); Purple_Bg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Unchanged, Bg => Terminal.Magenta)); Cyan_Bg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Unchanged, Bg => Terminal.Cyan)); Grey_Bg : constant String := Terminal.Get_ANSI_Sequence ((Style => Terminal.Reset_All, Fg => Terminal.Unchanged, Bg => Terminal.Grey)); -- The various colors that can be applied to text. You can combine a -- foreground and a background color by concatenating the strings. -- !!! This constants provided for backward compatibility. Use Style -- parameter instead in new applications. subtype Message_Style is GNATCOLL.Terminal.Full_Style; -- Styling applied to the text of a message. -- This has no effect if the stream does not support colors, or if -- the DEBUG.COLORS setting has not been enabled. Use_Default_Style : constant Message_Style := (Fg => GNATCOLL.Terminal.Unchanged, Bg => GNATCOLL.Terminal.Unchanged, Style => GNATCOLL.Terminal.Unchanged); -- Messages will use the default style declared for the handle, no -- overriding takes place Default_Block_Style : constant Message_Style := (Fg => GNATCOLL.Terminal.Unchanged, Bg => GNATCOLL.Terminal.Unchanged, Style => GNATCOLL.Terminal.Dim); procedure Trace (Handle : not null access Trace_Handle_Record'Class; E : Ada.Exceptions.Exception_Occurrence; Msg : String := "Unexpected exception: "; Style : Message_Style := Use_Default_Style); procedure Trace (Handle : not null access Trace_Handle_Record'Class; E : Ada.Exceptions.Exception_Occurrence; Msg : String := "Unexpected exception: "; Color : String) with Obsolescent; -- Extract information from the given Exception_Occurrence and output it -- with Msg as a prefix. -- You can override the default color used for the stream by specifying the -- color parameter. -- -- The output is really done on a separate handle with the same name as -- Handle and a suffix of ".EXCEPTIONS". This way, it is possible to -- configure this handle differently. For instance, the configuration file -- could contain: -- *.EXCEPTIONS=yes >&stdout -- to always display those exceptions on stdout and not on the default -- stream for Handle itself (a useful scenario when this version of Trace -- is used to display unexpected exceptions in your application). procedure Trace (Handle : not null access Trace_Handle_Record'Class; Message : String; Style : Message_Style := Use_Default_Style; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); procedure Trace (Handle : not null access Trace_Handle_Record'Class; Message : String; Color : String; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) with Obsolescent; -- Output Message to the stream associated with Handle, along with any -- extra information setup by the user (see the default handles below). -- If Handle is not active, this subprogram will do nothing. -- Likewise, this procedure will do nothing if Handle has no associated -- stream (which is the case when Parse_Config_File has not been called -- and no stream was specified in the call to Create). -- -- If message includes ASCII.LF characters, then several lines are output, -- starting with a special prefix -- -- Do not modify the parameters Location and Entity, they will have proper -- default values, and are used to output additional information about the -- context of the call. -- -- In case of exception (for instance because the log file is not -- writable), the behavior is controlled by the parameter On_Exception -- that was passed to Parse_Config_File. -- -- You can override this procedure if you systematically want to add extra -- information when logging via a specific handle. The other procedures -- like Assert, Increase_Indent and Decrease_Indent will call this -- procedure. procedure Assert (Handle : not null access Trace_Handle_Record'Class; Condition : Boolean; Error_Message : String; Message_If_Success : String := ""; Raise_Exception : Boolean := True; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) with Inline; -- Check that Condition is true. -- This subprogram does nothing if Handle is not active. -- If Condition is False: -- * Error_Message is output to a handle named Handle & ".EXCEPTIONS" -- just like an exception would be. This means that in general the -- message will be displayed in red. -- However, do not use this procedure just to get a red message. -- Instead use a standard Trace and specify the Style, or configure -- Handle to have red messages by default. -- * In addition, if Raise_Exception is true then an Assertion_Error -- exception is raised. -- -- If Condition is True: -- * Message_If_Success is output to Handle, if it isn't empty. procedure Increase_Indent (Handle : access Trace_Handle_Record'Class := null; Msg : String := ""; Style : Message_Style := Use_Default_Style; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); procedure Decrease_Indent (Handle : access Trace_Handle_Record'Class := null; Msg : String := ""; Style : Message_Style := Use_Default_Style; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity); -- Change the indentation level for traces with the same output stream. -- This is so that traces that result from other subprograms be slightly -- indented, so as to make the output more readable. The output would for -- instance look like: -- [HANDLE1] Procedure 1 -- [HANDLE2] Procedure 2 -- [HANDLE1] End of Procedure 1 -- If Handle and Msg are specified, a message is output on that handle to -- explain the change of indentation. The message is only displayed if the -- handle is active, but the indentation is always changed. function Count (Handler : not null access Trace_Handle_Record'Class) return Natural; -- Return the number of times that Trace was called on the handler. This -- count is only incremented when Handler is active. procedure Set_Active (Handle : not null access Trace_Handle_Record'Class; Active : Boolean) with Inline; -- Override the activation status for Handle. -- When not Active, the Trace function will do nothing. -- -- An active trace might still have no output if it doesn't have an -- associated stream, i.e. if Parse_Config_File was never called and -- no stream was specified in the call to Create. function Is_Active (Handle : not null access Trace_Handle_Record'Class) return Boolean with Inline; function Active (Handle : not null access Trace_Handle_Record'Class) return Boolean is (Debug_Mode and then Is_Active (Handle)) with Inline; -- Return True if traces for Handle are activated. -- This function can be used to avoid the evaluation of complex -- expressions in case traces are not active, as in the following -- code: -- if Active (Handle) then -- Trace (Handle, Message & Expensive_Computation); -- end if; -- -- Is_Active will check the flag on the trace handle, which is fast but -- can only be done dynamically. Active, on the other hand, also checks -- the Debug_Mode flag statically, so that if you have disable debugging -- altogether, the code will not even be inserted in the object code by -- the compiler. type Handlers_Proc is access procedure (Handle : Trace_Handle); procedure For_Each_Handle (Proc : not null Handlers_Proc); -- Calls Proc for all created trace handlers. ------------ -- Blocks -- ------------ type Block_Trace_Handle (<>) is limited private with Warnings => Off; subtype Block_Logger is Block_Trace_Handle; -- The aspect avoids warnings on unused instances, yet allows code to -- manipulate those instances when needed (which a "Unused=>True" would -- not) function Create (Handle : Logger; Message : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity; Style : Message_Style := Default_Block_Style) return Block_Logger; -- An object used to trace execution of blocks. -- This is a controlled object, which you should create first in your -- subprogram, and that will automatically finalize itself when the -- subprogram exists. For instance: -- Me : constant Logger := Create ("PKG"); -- procedure Foo (A : Integer) is -- Block_Me : constant Block_Logger := Create (Me); -- begin -- Trace (Me, "A=" & A'Img); -- if A > 1 then -- Foo (A - 1); -- end if; -- end Foo; -- Foo (2); -- -- which will automatically display in the traces : -- [PKG] Entering Foo:pkg.adb:5 -- [PKG] A= 2 -- [PKG] Entering Foo:pkg.adb:5 -- [PKG] A=1 -- [PKG] Leaving Foo:pkg.adb:5 -- [PKG] Leaving Foo:pkg.adb:5 -- -- Note the use of "with Unreferenced" in the above example (which could -- be replaced with a pragma Unreferenced). This is to avoid warnings from -- the compiler that the variable is unused, and is only necessary if you -- are compiling with -gnatwa or -gnatwm. -- -- Message can be used to display extra information. For efficiency -- reasons, it is not recommended to build the string dynamically to -- display the parameter of the enclosing subprograms, or perhaps as: -- -- procedure Foo (A, B, C : Integer) is -- Block_Me : constant Block_Logger := Create -- (Me, (if Active (Me) then A'Img & B'Img & C'Img else "")); -- begin -- null; -- end Foo; -- -- so that the string is only built if the trace is active. -- -- If the subprogram exits with an exception, no trace of the exception -- is displayed, you should still have an explicit exception handler if -- you want to Trace that exception. Of course, the "Leaving" message will -- be properly displayed. ------------- -- Streams -- ------------- type Typical_Msg_Size is mod 128; for Typical_Msg_Size'Size use 8; package Msg_Strings is new GNATCOLL.Strings_Impl.Strings (SSize => Typical_Msg_Size, Character_Type => Character, Character_String => String); -- We assume that most messages (including decorators) will be less than -- this number of characters, and optimize the string creation for this. -- But we still support larger messages, at a cost of one memory -- allocation which slows things down a bit. type Trace_Stream_Record is abstract tagged limited private; type Trace_Stream is access all Trace_Stream_Record'Class; -- A stream is an object responsible for ultimately displaying a string (as -- opposed to using Put_Line). Such objects do not need, in general, to be -- manipulated by your application, and you only access them by name (see -- the various descriptions above, and the parameter Stream in the call to -- Create). -- The various streams support various capabilities, and for instance not -- all of them can display colors. -- You could create your own stream if you want to redirect the traces to -- some specific area in your graphical application for instance, or -- because you want to output the logs on a socket and have them read by -- another application. -- A few predefined streams are provided in this package, and others in -- child packages (gnat-traces-syslog.ads for instance). procedure Put (Stream : in out Trace_Stream_Record; Str : Msg_Strings.XString) is abstract; -- Outputs a whole line to the stream. -- Str always ends up with a trailing newline. -- The stream needs to take appropriate lock or other synchronization -- mechanism to avoid mixing multiple lines of output. This lets each -- stream have its own lock, rather than a global lock, which improves -- the throughput. procedure Close (Stream : in out Trace_Stream_Record); -- Close the stream function Supports_Color (Stream : Trace_Stream_Record) return Boolean is (True); function Supports_Time (Stream : Trace_Stream_Record) return Boolean is (True); -- Whether the stream accepts color output, and whether we should output -- the time (if the user requested it). In some cases (syslog for instance) -- it isn't necessary to output the time, since that's already done -- automatically type Stream_Factory is abstract tagged null record; type Stream_Factory_Access is access all Stream_Factory'Class; function New_Stream (Factory : Stream_Factory; Args : String) return Trace_Stream is abstract; -- Return a newly allocated stream. -- Args is part of the string provided by the user in the configuration -- file (see below Register_Stream_Factory). -- The factory is never called twice with the same arguments, since this -- package will reuse existing streams whenever possible. procedure Register_Stream_Factory (Name : String; Factory : Stream_Factory_Access); -- Add Factory as one of the supported streams, available to Create or in -- the configuration files. This must be called before parsing the -- configuration file, of course. -- The following predefined streams are always registered: -- "&1": output to stdout (syntax similar to Unix) -- "&2": output to stderr -- "filename": output to a file named "filename" -- To avoid confusion with filenames, streams registered through this -- procedure will be available as: -- "&" & Name [ & ":" & Args ] -- The arguments are optional and can be used to further customize your -- stream. -- In the configuration file, you can redirect to any of the registered -- stream, either by default by putting the following on a line of its own: -- >&stream_name -- >&stream_name:args -- or for each specific stream: -- STREAM=yes >&stream_name -- STREAM=yes >&stream_name:args -- The object pointed by Factory will be freed by automatically when the -- factory container is freed. procedure Set_Default_Stream (Name : String; Config_File : GNATCOLL.VFS.Virtual_File := GNATCOLL.VFS.No_File); -- Set Name as the default stream. -- See Register_Stream_Factory for a list of valid names. The name can be -- prefixed with ">>" to append to that stream. -- An optional '>' is also allowed, although it is implicit if not -- specified. -- The Config_File is used to resolve a relative path name when -- needed. ---------------- -- Decorators -- ---------------- type Trace_Decorator_Record is new Trace_Handle_Record with private; type Trace_Decorator is access all Trace_Decorator_Record'Class; procedure Start_Of_Line (Self : in out Trace_Decorator_Record; Msg : in out Msg_Strings.XString; Is_Continuation : Boolean) is null; -- Called at the start of each line of the message. This procedure -- should modify Msg to Append extra information to it, if needed. -- GNATCOLL.Traces will then append the indentation for each line -- automatically, see Increase_Indent and Decrease_Indent. -- You can override this procedure to display a timestamp aligned at the -- beginning of the line, for instance. procedure Before_Message (Self : in out Trace_Decorator_Record; Handle : not null Logger; Msg : in out Msg_Strings.XString) is null; procedure After_Message (Self : in out Trace_Decorator_Record; Handle : not null Logger; Msg : in out Msg_Strings.XString) is null; -- You can override either of these two procedures to create your own -- decorators to specific trace handles (i.e. additional information) each -- time some message is logged. These functions are only called for the -- handles passed to Add_Global_Decorator. -- -- When displayed in the log, a line looks like: -- -- [HANDLE_NAME] MESSAGE -- -- The prefix decorator is in charge of displaying blank spaces to -- indent the line (see Increase_Indent and Decrease_Indent). But you -- can also use it to display other pieces of information (like a -- timestamp if you always want them aligned for instance). -- Any global decorator will be called before the indentation (so at -- column 1). -- Indent is the indentation level (1, 2, 3,...). This isn't the -- number of columns to indent. -- -- Only the prefix_decorator is called on continuation lines (when a -- message doesn't fit on a single line). procedure Add_Global_Decorator (Decorator : not null Trace_Decorator; Name : String); -- Register a global decorator that will apply to all existing -- Trace_Handle. The decorator only has an effect when it is active. -- Here is an example: -- type My_Decorator is new Trace_Decorator_Record with null record; -- overriding procedure Before_Message -- (Self : in out My_Decorator; -- Handle : not null Logger; -- Message : in out Msg_Strings.XString) is -- begin -- Msg_Strings.Append (Message, "Some info"); -- end Before_Message; -- -- Add_Global_Decorator (new My_Decorator, "MY_DECO"); -- -- And then you can use your configuration file as usual to activate or -- deactivate the "MY_DECO" handle. -- -- A decorator is disabled by default (when it is registered via this -- procedure). To active, you can either do this in the configuration -- file, with the usual: -- MY_DECO=yes -- or in the code, as: -- Set_Active (Create ("MY_DECO"), True); private type Trace_Stream_Record is abstract tagged limited record Name : GNAT.Strings.String_Access; Next : Trace_Stream; Indentation : aliased Atomic_Counter := 0; -- Current indentation for stream end record; -- Name is the full name including the arguments, for instance "file:foo" -- if the user has defined a stream called "file" with a parameter "foo" type Block_Trace_Handle is new Ada.Finalization.Limited_Controlled with record Me : Logger; Loc : GNAT.Strings.String_Access; Style : Message_Style; end record; overriding procedure Finalize (Self : in out Block_Logger); type Trace_Handle_Record is tagged limited record Next : Logger; -- linked list Name : GNAT.Strings.String_Access; Timer : Ada.Calendar.Time; Stream : Trace_Stream; -- null for default stream Exception_Handle : Logger; -- The handle used when calling Trace and passing an exception -- occurrence. This has Name & ".EXCEPTIONS" as a name, and is created -- the first time it is needed. Count : aliased Atomic_Counter; Default_Style : Message_Style; Finalize : Boolean; Active : Boolean; Forced_Active : Boolean; Stream_Is_Default : Boolean; With_Colors : Boolean := False; With_Time : Boolean := False; -- Compute values, from the stream and corresponding settings. These -- are used to avoid dispatching calls in Log. end record; pragma Pack (Trace_Handle_Record); -- If Forced_Active is true, then the Active status shouldn't be impacted -- by a '+' in the configuration file type Trace_Decorator_Record is new Trace_Handle_Record with null record; end GNATCOLL.Traces; gnatcoll-core-21.0.0/src/gnatcoll-vfs_utils.adb0000644000175000017500000002026113661715457021263 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Unchecked_Conversion; with Ada.Directories; use Ada.Directories; with GNATCOLL.Remote; use GNATCOLL.Remote; with GNATCOLL.Remote.Db; use GNATCOLL.Remote.Db; with GNATCOLL.Utils; with GNATCOLL.VFS_Types; package body GNATCOLL.VFS_Utils is function Unchecked is new Ada.Unchecked_Conversion (String_Access, Filesystem_String_Access); ------------------------ -- Normalize_Pathname -- ------------------------ function Normalize_Pathname (Name : Filesystem_String; Directory : Filesystem_String := ""; Resolve_Links : Boolean := True; Case_Sensitive : Boolean := True) return Filesystem_String is begin return +Normalize_Pathname (+Name, +Directory, Resolve_Links, Case_Sensitive); end Normalize_Pathname; ---------------------- -- Is_Absolute_Path -- ---------------------- function Is_Absolute_Path (Name : Filesystem_String) return Boolean is begin return Is_Absolute_Path (+Name); end Is_Absolute_Path; --------------------- -- Is_Regular_File -- --------------------- function Is_Regular_File (Name : Filesystem_String) return Boolean is begin return Is_Regular_File (+Name); end Is_Regular_File; ------------------ -- Is_Directory -- ------------------ function Is_Directory (Name : Filesystem_String) return Boolean is begin return Is_Directory (+Name); end Is_Directory; --------------- -- Copy_File -- --------------- procedure Copy_File (Name : Filesystem_String; Pathname : Filesystem_String; Success : out Boolean; Mode : Copy_Mode := Copy; Preserve : Attribute := Time_Stamps) is begin Copy_File (+Name, +Pathname, Success, Mode, Preserve); end Copy_File; ------------------ -- Set_Writable -- ------------------ procedure Set_Writable (Name : Filesystem_String) is begin Set_Writable (+Name); end Set_Writable; ---------------------- -- Set_Non_Writable -- ---------------------- procedure Set_Non_Writable (Name : Filesystem_String) is begin Set_Non_Writable (+Name); end Set_Non_Writable; ---------------------- -- Create_Temp_File -- ---------------------- procedure Create_Temp_File (FD : out File_Descriptor; Name : out Filesystem_String_Access) is R : String_Access; begin Create_Temp_File (FD, R); Name := Unchecked (R); end Create_Temp_File; -------------------- -- File_Extension -- -------------------- function File_Extension (Path : Filesystem_String) return Filesystem_String is begin return +File_Extension (+Path); end File_Extension; --------------------- -- Get_Current_Dir -- --------------------- function Get_Current_Dir return Filesystem_String is begin return +Get_Current_Dir; end Get_Current_Dir; ----------------------- -- Name_As_Directory -- ----------------------- function Name_As_Directory (Name : Filesystem_String) return Filesystem_String is begin if +Name = "" then return ""; end if; return Filesystem_String (GNATCOLL.Path.Ensure_Directory (GNATCOLL.Path.Local_FS, GNATCOLL.VFS_Types.FS_String (Name))); end Name_As_Directory; -------------- -- Dir_Name -- -------------- function Dir_Name (Path : Filesystem_String) return Filesystem_String is begin return +Dir_Name (+Path); end Dir_Name; --------------- -- Base_Name -- --------------- function Base_Name (Path : Filesystem_String; Suffix : Filesystem_String := "") return Filesystem_String is begin return +Base_Name (+Path, +Suffix); end Base_Name; ---------------- -- Change_Dir -- ---------------- procedure Change_Dir (Dir_Name : Filesystem_String) is begin Change_Dir (+Dir_Name); end Change_Dir; --------------------- -- Format_Pathname -- --------------------- function Format_Pathname (Path : Filesystem_String; Style : Path_Style := System_Default) return Filesystem_String is begin return +Format_Pathname (+Path, Style); end Format_Pathname; ---------- -- Open -- ---------- procedure Open (Dir : out Dir_Type; Dir_Name : Filesystem_String) is begin Open (Dir, +Dir_Name); end Open; ------------------------- -- Locate_Exec_On_Path -- ------------------------- function Locate_Exec_On_Path (Exec_Name : Filesystem_String) return Filesystem_String_Access is Val : String_Access := Locate_Exec_On_Path (+Exec_Name); begin if Val /= null then declare Ret : constant Filesystem_String_Access := new Filesystem_String'(+Val.all); begin Free (Val); return Ret; end; end if; return null; end Locate_Exec_On_Path; ------------------------- -- Locate_Regular_File -- ------------------------- function Locate_Regular_File (File_Name : Filesystem_String; Path : Filesystem_String) return Filesystem_String_Access is Val : String_Access := Locate_Regular_File (+File_Name, +Path); Ret : Filesystem_String_Access; begin if Val /= null then Ret := new Filesystem_String'(+Val.all); Free (Val); return Ret; else return null; end if; end Locate_Regular_File; ------------- -- Compose -- ------------- function Compose (Containing_Directory : Filesystem_String := ""; Name : Filesystem_String; Extension : Filesystem_String := "") return Filesystem_String is begin return +Compose (+Containing_Directory, +Name, +Extension); end Compose; ----------------------- -- Is_Case_Sensitive -- ----------------------- function Is_Case_Sensitive (Host : String) return Boolean is FS : GNATCOLL.VFS_Types.FS_Type; begin if Host = Local_Host then FS := GNATCOLL.Path.Local_FS; else FS := Get_Server (Host).Shell_FS; end if; return GNATCOLL.Path.Is_Case_Sensitive (FS); end Is_Case_Sensitive; ---------------- -- File_Equal -- ---------------- function File_Equal (F1, F2 : Filesystem_String; Host : String) return Boolean is begin return GNATCOLL.Utils.Equal (+F1, +F2, Is_Case_Sensitive (Host)); end File_Equal; end GNATCOLL.VFS_Utils; gnatcoll-core-21.0.0/src/gnatcoll-io-native.ads0000644000175000017500000001510613743647711021161 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ private with GNATCOLL.Path; package GNATCOLL.IO.Native is type Native_File_Record is new File_Record with private; function Create (Path : FS_String) return File_Access; ------------------------------ -- Utilities for native FS -- ------------------------------ function Current_Dir return File_Access; -- Return the current directory function Home_Dir return File_Access; -- Return the home directory function Get_Tmp_Directory return File_Access; -- Return the directory that can be used to store temporary -- files on the filesystem. function Get_Logical_Drives return File_Array; Local_Root_Dir : constant File_Access; ---------------------------- -- Overridden from parent -- ---------------------------- overriding function Dispatching_Create (Ref : not null access Native_File_Record; Full_Path : FS_String) return File_Access; overriding function To_UTF8 (Ref : not null access Native_File_Record; Path : FS_String) return String; overriding function From_UTF8 (Ref : not null access Native_File_Record; Path : String) return FS_String; overriding function Is_Local (File : Native_File_Record) return Boolean; overriding function Get_FS (File : not null access Native_File_Record) return FS_Type; overriding procedure Resolve_Symlinks (File : not null access Native_File_Record); overriding function Is_Regular_File (File : not null access Native_File_Record) return Boolean; overriding function Size (File : not null access Native_File_Record) return Long_Integer; overriding function Is_Directory (File : not null access Native_File_Record) return Boolean; overriding function Is_Symbolic_Link (File : not null access Native_File_Record) return Boolean; overriding function File_Time_Stamp (File : not null access Native_File_Record) return Ada.Calendar.Time; overriding function Is_Writable (File : not null access Native_File_Record) return Boolean; overriding procedure Set_Writable (File : not null access Native_File_Record; State : Boolean); overriding function Is_Readable (File : not null access Native_File_Record) return Boolean; overriding procedure Set_Readable (File : not null access Native_File_Record; State : Boolean); overriding procedure Rename (From : not null access Native_File_Record; Dest : not null access Native_File_Record; Success : out Boolean); overriding procedure Copy (From : not null access Native_File_Record; Dest : FS_String; Success : out Boolean); overriding procedure Delete (File : not null access Native_File_Record; Success : out Boolean); overriding function Read_Whole_File (File : not null access Native_File_Record) return GNAT.Strings.String_Access; overriding function Read_Whole_File (File : not null access Native_File_Record) return GNATCOLL.Strings.XString; overriding procedure Open_Write (File : not null access Native_File_Record; Append : Boolean := False; FD : out GNAT.OS_Lib.File_Descriptor; Error : out Ada.Strings.Unbounded.Unbounded_String); overriding procedure Close (File : not null access Native_File_Record; FD : GNAT.OS_Lib.File_Descriptor; Success : out Boolean); overriding function Change_Dir (Dir : not null access Native_File_Record) return Boolean; overriding function Read_Dir (Dir : not null access Native_File_Record; Dirs_Only : Boolean := False; Files_Only : Boolean := False) return GNAT.Strings.String_List; overriding function Make_Dir (Dir : not null access Native_File_Record; Recursive : Boolean) return Boolean; overriding procedure Remove_Dir (Dir : not null access Native_File_Record; Recursive : Boolean; Success : out Boolean); overriding procedure Copy_Dir (From : not null access Native_File_Record; Dest : FS_String; Success : out Boolean); overriding procedure Copy_File_Permissions (From, To : not null access Native_File_Record; Success : out Boolean); package Codec is function To_UTF8 (Path : FS_String) return String; function To_UTF8 (Path : Wide_String) return String; function From_UTF8 (Path : String) return FS_String; function From_UTF8 (Path : String) return Wide_String; end Codec; -- Codec to translate a path to/from utf-8 private type Native_File_Record is new File_Record with null record; Local_Root_Dir : constant File_Access := new Native_File_Record' (Ref_Count => 1, Full => new FS_String' (GNATCOLL.Path.Path (GNATCOLL.Path.Local_FS, "", "", "")), Normalized => null, Normalized_And_Resolved => null, Kind => Directory); end GNATCOLL.IO.Native; gnatcoll-core-21.0.0/src/gnatcoll-format_columns_vertical.ads0000644000175000017500000000407713661715457024216 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- Procedure to format ordered words by columns vertically. -- Number of columns limited by width and have to be calculated to minimize -- number of rows in each column. with GNATCOLL.Strings; with GNATCOLL.Formatters; procedure GNATCOLL.Format_Columns_Vertical is new Formatters.Columns_Vertical (Strings); -- See comment for GNATCOLL.Formatters.Columns_Vertical gnatcoll-core-21.0.0/src/terminals.c0000644000175000017500000001144313661715457017140 0ustar nicolasnicolas/*---------------------------------------------------------------------------- -- G N A T C O L L -- -- -- -- Copyright (C) 2014-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -----------------------------------------------------------------------------*/ #ifdef _WIN32 #include #include #include #else #include #ifdef HAVE_TERMIOS_H #include // for TIOCGWINSZ on some systems #endif #include #include #endif int gnatcoll_get_console_screen_buffer_info(int forStderr) { #ifdef _WIN32 const HANDLE handle = GetStdHandle (forStderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbiInfo; if (GetConsoleScreenBufferInfo (handle, &csbiInfo)) { return csbiInfo.wAttributes; } #else return -1; #endif } void gnatcoll_set_console_text_attribute(int forStderr, int attrs) { #ifdef _WIN32 const HANDLE handle = GetStdHandle (forStderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); SetConsoleTextAttribute (handle, (WORD)attrs); #endif } int gnatcoll_terminal_has_colors(int fd) { #ifdef _WIN32 return _isatty(fd); #else // Ideally, we should check the terminfo database and check the // max_colors fields (from the command line, this is done with // "tput colors"). However, this is fairly complex, and would // drag in the curses library. // For now, let's just assume that a tty always supports colors, // which is true in this day and age for interactive terminals on // all Unix platforms. A pipe will return 0 below, so will not have // colors by default. // ??? We could also check the value of the TERM environment variable, // but this is very approximate at best. return isatty(fd); #endif } void gnatcoll_beginning_of_line(int forStderr) { #ifdef _WIN32 const HANDLE handle = GetStdHandle (forStderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbiInfo; if (GetConsoleScreenBufferInfo (handle, &csbiInfo)) { csbiInfo.dwCursorPosition.X = 0; SetConsoleCursorPosition(handle, csbiInfo.dwCursorPosition); } #else // struct winsize ws; // ioctl(forStderr ? 2 : 1, TIOCGWINSZ, &ws); if (write(forStderr ? 2 : 1, "\r", 1) != 1) { // Ignore failure for now } #endif } void gnatcoll_clear_to_end_of_line(int forStderr) { #ifdef _WIN32 const HANDLE handle = GetStdHandle (forStderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbiInfo; if (GetConsoleScreenBufferInfo (handle, &csbiInfo)) { DWORD numberOfCharsWritten; FillConsoleOutputCharacter( handle, ' ', csbiInfo.dwSize.X - csbiInfo.dwCursorPosition.X + 1, // length csbiInfo.dwCursorPosition, // dWriteCoord &numberOfCharsWritten); } #else if (write(forStderr ? 2 : 1, "\033[0K", 4) != 4) { // Ignore failure for now } #endif } int gnatcoll_terminal_width(int forStderr) { #ifdef _WIN32 const HANDLE handle = GetStdHandle (forStderr ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbiInfo; if (GetConsoleScreenBufferInfo (handle, &csbiInfo)) { return (int)csbiInfo.dwSize.X; } return -1; #else #ifdef TIOCGWINSZ struct winsize w; ioctl(forStderr ? 1 : 0, TIOCGWINSZ, &w); return w.ws_col; #else return -1; #endif #endif } gnatcoll-core-21.0.0/src/gnatcoll-projects.ads0000644000175000017500000033723613743647711021132 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2002-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides an API to manipulate project files. -- -- Projects are complex objects, and thus this API is extensive. A number of -- simple subprograms are provided to access the most needed features (list -- of files for instance), but it might be necessary to go into more complex -- subprograms to access some properties of the projects. -- -- Projects and Project_Trees -- ========================== -- -- A project is rarely used on its own. Most often, it is part of a whole set -- of projects, importing each other, each of which is responsible for a small -- part of the overall application. In the rest of this API, a Project is one -- single .gpr file. A Project_Tree is a set of projects related through -- "with" or "limited with" relations. This really is a cyclic graph, where -- cycles can only occur through "limited with"s. -- -- Multiple project trees can be loaded in memory at the same time. -- -- Loading a Project_Tree (Project views) -- ====================================== -- -- One never loads a project, we only ever load Project_Trees, as a consistent -- whole. Loading a project is done in two steps -- - create a syntactic tree representation in memory -- - resolve the tree in the current scenario. The scenario can modify the -- value of the attributes, the list of source dirs and source files,... -- but never the relationships between projects. The result is called the -- "Project View". -- The first phase is extremely fast, whereas the second phase can take -- several seconds (or more) on some complex projects when source files are -- on remote file systems. -- When you are doing modifications to an in-memory project, you should in -- fact be doing the changes on the syntactic tree, and then recompute the -- project view. Therefore, this API does not try to hide the two-steps -- process, which are needed in some cases. -- -- example: -- -- with GNATCOLL.VFS; use GNATCOLL.VFS; -- -- Tree : Project_Tree; -- -- Tree.Load (GNATCOLL.VFS.Create (+"/usr/local/projects/default.gpr"); -- -- Project attributes -- ================== -- -- A project's attributes describe all its properties. This API provides -- various ways to access the attributes. Some of them are so often used that -- specific subprograms exist (source files, source dirs,...), whereas in some -- other cases you will have to use the more general API found later in this -- package. -- -- For instance, after you have loaded the project as above, here is how you -- can find all the directories that might contain source files: -- -- Dirs : File_Array := -- Source_Dirs (Tree.Root_Project, Recursive => True); private with Ada.Containers.Indefinite_Hashed_Maps; with Ada.Containers.Doubly_Linked_Lists; with Ada.Containers.Indefinite_Ordered_Sets; with Ada.Containers.Vectors; private with Ada.Strings.Hash; private with Ada.Finalization; with Ada.Unchecked_Deallocation; with GNAT.Expect; with GNAT.Strings; with GNAT.OS_Lib; with GNATCOLL.VFS; private with GPR.Tree; private with GPR; package GNATCOLL.Projects is All_Packs : constant GNAT.Strings.String_List_Access; -- When used as the value of argument Packages_To_Check in procedures Load -- and Add_Imported_Project, all packages and attributes are checked. Any -- unknown package or attribute will result in an error. No_Packs : constant GNAT.Strings.String_List_Access; -- Default value for argument Packages_To_Check in procedures Load -- and Add_Imported_Project. All unknown packages and attributes will be -- ignored. type Project_Environment is tagged private; type Project_Environment_Access is access all Project_Environment'Class; -- This type describes the conditions under which a project is loaded. This -- includes scenario variables, various settings that affect the loading -- (trusted mode,...) as well as the default source and object directories -- in which the runtime files can be found. -- This environment might be common to a set of project trees loaded at the -- same time in memory. -- You can already create such types via the Initialize subprogram below. -- However, a default environment will be build automatically if you do -- not provide one when parsing a project. -- If you subclass this type, you should still call Initialize after -- allocating a variable of this type. type Project_Tree is tagged private; type Project_Tree_Access is access all Project_Tree'Class; -- A set of project files, related through "with"s or "limited with"s. -- This is a tagged object so that you can use the Ada05 dotted notation to -- access its primitive operations, and so that you can add your own -- fields (or precompute some data that you want to reuse). -- In practice, this is not necessarily a "tree" in the data structure -- sense, more like a graph, but the term Tree makes it more obvious that -- one of the projects plays a special role, the root project. procedure Initialize (Self : in out Project_Environment_Access; IDE_Mode : Boolean := False); -- Allocate a new environment (if Self is null) and initialize internal -- data. -- IDE_Mode turns on some additional functionality such as extra error -- message filtering that is only relevant for interactive applications -- such as IDE. Most of the tools that do a single pass on the project -- should not turn on this flag. procedure Free (Self : in out Project_Environment_Access); procedure Free (Self : in out Project_Tree_Access); -- Free memory allocated for the pointer. You should first unload the tree. type Project_Type is tagged private; type Project_Type_Access is access all Project_Type'Class; No_Project : aliased constant Project_Type; -- This type represents a single .gpr project file, which is part of a -- Project_Tree. -- A Project_Type only makes sense in the context of a tree, so it contains -- an implicit reference to the tree that was used to load it. -- -- This type is tagged, so that you can use Ada05 dotted notation (and -- it is implemented as a controlled type internally for reference -- counting). However, you cannot extend it (because instances are created -- implicitly by Load). If you need to add custom data to projects, see the -- use of Data_Factory below. -- It is always safe to store an instance of Project_Type in your records, -- you do not need to store an access on Project_Type'Class. Invalid_Project : exception; -- raised when attempting to load an invalid project. type Error_Report is access procedure (Msg : String); -- Callback used to report warnings and error messages to the caller. ---------------------- -- Loading projects -- ---------------------- -- The following subprograms provide ways to load projects. -- In particular, they give access to the two phases of the loading, as -- described in the general comments of this package. procedure Load (Self : in out Project_Tree; Root_Project_Path : GNATCOLL.VFS.Virtual_File; Env : Project_Environment_Access := null; Packages_To_Check : GNAT.Strings.String_List_Access := No_Packs; Errors : Error_Report := null; Recompute_View : Boolean := True; Report_Missing_Dirs : Boolean := True); -- Load a new set of project files, starting from a root project. -- Root_Project_Path is either an absolute path, or relative to the current -- directory. It should point to a readable existing file. -- The two steps of the loading (see general description of this package) -- are performed automatically. -- If the project itself or some of its dependencies should be found on the -- project path, the latter should be initialized properly (if you have -- already loaded a project, you might want to reuse the environment by -- passing a non-empty Env parameter). -- -- A list of packages where all the attributes declared must be recognized -- may be indicated by Packages_To_Check. By default, no package is -- checked. There may be unknown attributes in packages that are not -- included in Packages_To_Check. If a value given for Packages_To_Check -- has been allocated, this value may be freed immediately after the call -- to Load if it is no longer needed. -- -- Errors and warnings that occur during loading are reported through the -- Errors callback. If the project could not be loaded, the exception -- Invalid_Project is then raised. In such a case, any project set -- previously loaded is still in memory. -- -- If no value is provided for Env, a default one will be created -- automatically. Passing a value is useful if you need to share the -- environment between separate project trees. This default value will -- never be freed though, resulting in a potential memory leak. -- -- If that project is already loaded in Self, it will be reloaded if any of -- the .gpr files have changed on disk (see also Reload_If_Needed). -- -- The previous project is automatically unloaded, and existing instances -- of Project_Type become invalid and should not be used anymore. -- -- If Recompute_View is False, the subprogram Recompute_View will not be -- called automatically. This gives you a chance to do some dynamic -- changes on the project (changing attributes for instance), even though -- you will need to call Recompute_View yourself. -- -- If Report_Missing_Dirs is true, then a warning will be issued when a -- project file's object directory does not exist yet. Note that this flag -- will be stored in the project environment and will have an effect on -- further calls to Recompute_View with the same project environment. procedure Set_Trusted_Mode (Self : in out Project_Environment; Trusted : Boolean := True); function Trusted_Mode (Self : Project_Environment) return Boolean; -- Set/Get the trusted mode for the project set: -- If it is True, then it is assumed that no links are used in the project, -- and that directory names cannot match file names according to the -- naming scheme. This provides much faster loading. -- The default is True. procedure Reload_If_Needed (Self : in out Project_Tree; Reloaded : out Boolean; Recompute_View : Boolean := False; Errors : Error_Report := null); -- If any of the project files have changed on the disk, reloads the whole -- project tree. This performs the two phases of the loading. -- On exit, Reloaded is set to false if no reloading took place. procedure Load_Empty_Project (Self : in out Project_Tree; Env : Project_Environment_Access := null; Name : String := "empty"; Recompute_View : Boolean := True); -- Load an empty project. -- There is no source .gpr file corresponding to that project, which is -- created in memory. It has no source file. In general this procedure is -- used to initialize a usable and valid project tree, which the user will -- later replace with an actual project. -- A default version of Env will be created if null is passed. procedure Load_Implicit_Project (Self : in out Project_Tree; Env : Project_Environment_Access := null; Recompute_View : Boolean := True); -- Load special project _default.gpr that is used by gprbuild when invoked -- without -P switch. When implicit project file is used current directory -- is considered to be a source dir and an object dir. -- This mode is needed when you want to get easy access to Ada sources -- located in current dir without creating a temporary project file. procedure Recompute_View (Self : in out Project_Tree; Errors : Error_Report := null); -- Recompute the view of the project (the second phase of the loading). -- This does not change the in-memory syntactic tree of the project, but -- based on the current value of the scenario variables it might change the -- list of source files, source directories,... -- This procedure only needs to be called after you have modified the -- project in memory. It is automatically called by the various Load* -- subprograms. procedure Unload (Self : in out Project_Tree); -- Unload the project loaded in Self, and free the associated memory. -- No project is accessible through this tree once this has been called, -- and existing instances of Project_Type have become invalid. procedure Finalize; -- This is a dummy procedure. It is retained for easy compatibility with -- clients who used to call Finalize when this call was required. type Project_Status is (From_File, Default, From_Executable, Empty); function Status (Self : Project_Tree) return Project_Status; procedure Set_Status (Self : Project_Tree; Status : Project_Status); -- How the project was created: either read from a file, automatically -- created from a directory, automatically created from an executable -- (debugger case), or default empty project. An actual project file exists -- on disk only in the From_File or Default cases. function Is_Aggregate_Project (Self : Project_Type) return Boolean; -- Return true if the current project is an aggregate project or a library -- aggregate project. function Is_Aggregate_Library (Self : Project_Type) return Boolean; -- Return true if the current project is an aggregate library project. function Is_Abstract_Project (Self : Project_Type) return Boolean; -- Return true if the current project is an abstract project. function Get_Environment (Self : Project_Type) return Project_Environment_Access; -- Return the environment which applies to the project, or null ------------------ -- Project data -- ------------------ -- To make it easier to store instances of Project_Type in a data -- structure, that type is not visibly tagged (you do not have to store an -- access to Project_Type'Class and find out when you can free it). -- However, it might be convenient to associate your own custom data with a -- project (for instance extra caches for attributes that your application -- uses often, or other type of data). -- To do so, you should subclass Project_Data, as well as Project_Tree. For -- the latter, override the Data_Factory function to create a new instance -- of Project_Data. One such new instance will be associated with each -- projects that are loaded in the tree, and you can retrieve each -- project's own data with the Data function below. -- For instance: -- type My_Project_Data is new Project_Data with record -- ... -- end record; -- -- type My_Project_Tree is new Project_Tree with null record; -- overriding function Data_Factory -- (Self : My_Project_Tree) return Project_Data_Access is -- begin -- return new My_Project_Data; -- end Data_Factory; -- -- Tree : My_Project_Tree; -- Tree.Load (Create ("/usr/local/project.gpr")); -- -- Data : My_Project_Data := My_Project_Data -- (Data (Tree.Root_Project).all); type Project_Data is tagged private; type Project_Data_Access is access Project_Data'Class; function Data_Factory (Self : Project_Tree) return Project_Data_Access; -- Returns a new instance of Project_Data. -- This can be overridden if you want to store additional data in a -- project. In this case, you should create your own child of Project_Data, -- and return an instance of that child from this factory. -- This function is called implicitly by Load whenever a new project file -- is parsed and a new instance of Project_Type created. function Data (Project : Project_Type) return Project_Data_Access; -- Return the data associated with the project. You must not free the -- resulting pointer. procedure On_Free (Self : in out Project_Data); -- Called when Self needs to be freed. If you have subclassed Project_Data, -- you should override this procedure to free the data. You also need to -- call the inherited version of On_Free. ---------------------- -- Predefined paths -- ---------------------- -- Some directories are implicitly part of a project. These are in general -- the default directories used by compilers to access their runtime or -- look for other projects. -- You must tell GNATCOLL what those predefined directories are, although -- some facilities are provided to automatically extract them from gnatls -- in the case of GNAT Pro for Ada. -- When using C, you might want to add /usr/include to the predefined paths -- for instance. procedure Set_Predefined_Source_Path (Self : in out Project_Environment; Path : GNATCOLL.VFS.File_Array); procedure Set_Predefined_Object_Path (Self : in out Project_Environment; Path : GNATCOLL.VFS.File_Array); procedure Set_Predefined_Project_Path (Self : in out Project_Environment; Path : GNATCOLL.VFS.File_Array); -- Set the predefined environment. -- This should be called after loading the project. function Predefined_Source_Path (Self : Project_Environment) return GNATCOLL.VFS.File_Array; function Predefined_Object_Path (Self : Project_Environment) return GNATCOLL.VFS.File_Array; function Predefined_Project_Path (Self : Project_Environment) return GNATCOLL.VFS.File_Array; -- Return the predefined paths, or the current directory if no -- paths have been set yet. procedure Invalidate_Gnatls_Cache (Self : in out Project_Environment); -- Forces the recomputation of the predefined paths via gnatls. -- This should be called prior to calling Recompute_View, when the -- environment has changed (ADA_PROJECT_PATH, running gnatls on a -- different host,...) procedure Set_Default_Gnatls (Self : in out Project_Environment; Gnatls : String); No_Gnatls : constant String; -- Set the default gnatls to run (before a project is loaded). -- This impacts the default path on which projects are looked for, but -- will be overridden if the user has specified an IDE.Gnatlist attribute -- in his project. -- This procedure is now deprecated, and we recommend that project use the -- Runtime and Target attributes instead. See Set_Target_And_Runtime below. -- When No_Gnatls is set no attempts to invoke gnatls are made when loading -- a project. procedure Set_Target_And_Runtime (Self : in out Project_Environment; Target : String := ""; Runtime : String := ""); -- Override the Runtime and Target attributes. These values take priority -- over what is defined in the project file. -- These are generally set from --target and --RTS command line switches. procedure Set_Path_From_Gnatls (Self : in out Project_Environment; Gnatls : String; GNAT_Version : out GNAT.Strings.String_Access; Errors : Error_Report := null); -- Execute the given "gnatls" command with switch "-v" and parse the -- default search paths and project path from it. -- This function returns the version of GNAT as read from gnatls. This -- string must be freed by the user (Set_GNAT_Version is also called). procedure Set_Path_From_Gnatls_Output (Self : in out Project_Environment; Output : String; Host : String := GNATCOLL.VFS.Local_Host; GNAT_Version : out GNAT.Strings.String_Access); -- Same as Set_Path_From_Gnatls, but gets the output of "gnatls -v" in -- input (and does not spawn a command). -- This procedure also calls Set_GNAT_Version. procedure Spawn_Gnatls (Self : Project_Environment; Fd : out GNAT.Expect.Process_Descriptor_Access; Gnatls_Args : GNAT.OS_Lib.Argument_List_Access; Errors : Error_Report); -- Spawns the gnatls command passed in argument. -- This subprogram can be overridden if gnatls needs to be spawned on -- another machine (the default is to spawn on the local machine). function Gnatls_Host (Self : Project_Environment) return String; -- Returns the name of the remote host configuration responsible for -- executing gnatls. By default, returns the local host. procedure Set_GNAT_Version (Self : in out Project_Environment; Version : String) is null; -- This procedure is called when the project manager spawns and parses -- gnatls. At that point, it finds the version of GNAT and calls this -- subprogram, which you can override if you wish to store that version -- somewhere. ------------------------ -- Project properties -- ------------------------ -- The following subprograms give access to general properties of the -- project. See the section below to get access to the project's attributes Project_File_Extension : constant GNATCOLL.VFS.Filesystem_String; overriding function "=" (Prj1, Prj2 : Project_Type) return Boolean; -- Return true if Prj1 and Prj2 reference the same project function Name (Project : Project_Type) return String; -- Return the name of the project. function Project_Path (Project : Project_Type; Host : String := GNATCOLL.VFS.Local_Host) return GNATCOLL.VFS.Virtual_File; -- Return the path to the project file -- If Host is given, the path will be the one on the specified host. function Extended_Project (Project : Project_Type) return Project_Type; -- Return the project extended by project, or No_Project is there is none. -- If Project is an "extends all", this will return the project mentioned -- in the "extends all" clause, in general the root of the extended project -- tree. function Extending_Project (Project : Project_Type; Recurse : Boolean := False) return Project_Type; -- Return the project that extends Project, or No_Project if Project is not -- extended within the hierarchy and Recurse is False. -- This is in the context of the Project_Tree in which Project was loaded, -- so there can be at most one extending project. -- If Recurse is True, then the lowest possible project is returned, even -- if it is Project itself. This is useful when looking for specific source -- files. function Externally_Built (Project : Project_Type) return Boolean; -- Return whether Project is marked as externally built (project -- attribute Externally_Built set to "true"). ----------------- -- Directories -- ----------------- function Source_Dirs (Project : Project_Type; Recursive : Boolean := False; Include_Externally_Built : Boolean := True) return GNATCOLL.VFS.File_Array; pragma Precondition (Project /= No_Project); -- Return the list of source directories. -- The directories are returned in the order in which they are defined in -- the project files, so that in the case of Ada files the file will first -- be searched in the first directories, and if not found in the second,... -- If Recursive is True, the source directories of the subtree rooted at -- Project (ie all the projects imported directly or indirectly by Project) -- will also be returned, but in this case the order of the directories in -- the result is undefined and the result cannot be considered as a search -- path for project sources. -- If Include_Externally_Built is False then source directories belonging -- to project marked "Externally_Built" will not be returned. -- Note that duplicate directories might be returned when directories are -- shared by multiple projects in the same tree. function Directory_Belongs_To_Project (Self : Project_Tree; Directory : GNATCOLL.VFS.Filesystem_String; Direct_Only : Boolean := True) return Boolean; -- True if Directory belongs to one of the projects in the hierarchy. -- If Direct_Only is False, then True is returned if one of the -- subdirectories belong to the project, even if directory itself doesn't. -- This function is much more efficient than retrieving the source -- directories and doing the computation yourself, since it uses cached -- data. procedure Set_Build_Tree_Dir (Self : in out Project_Environment; Dir : GNATCOLL.VFS.Filesystem_String); function Build_Tree_Dir (Self : Project_Environment) return GNATCOLL.VFS.Filesystem_String; -- Getter/Setter to control the root directory for building out-of-tree -- projects. All relative object directories will be rooted at this -- location. procedure Set_Root_Dir (Self : in out Project_Environment; Dir : GNATCOLL.VFS.Filesystem_String); function Root_Dir (Self : Project_Environment) return GNATCOLL.VFS.Filesystem_String; -- Is only relevant when Build_Tree_Dir is set, this is used to keep -- information about the root directory of artifacts to properly relocate -- them. procedure Set_Object_Subdir (Self : in out Project_Environment; Subdir : GNATCOLL.VFS.Filesystem_String); function Object_Subdir (Self : Project_Environment) return GNATCOLL.VFS.Filesystem_String; -- The same project can be used in multiple contexts. In particular, the -- command line tools support the switch --subdirs, so that the sources can -- be built differently in various scenarios while putting the resulting -- object files in a separate object directory every time. -- This procedure lets you specify the name of a subdirectory of the object -- directory in which the object files are currently put. This directory -- is automatically taken into account by the Object_Path function below. procedure Set_Xrefs_Subdir (Self : in out Project_Environment; Subdir : GNATCOLL.VFS.Filesystem_String); function Xrefs_Subdir (Self : Project_Environment) return GNATCOLL.VFS.Filesystem_String; -- This is similar to Set_Object_Subdir, but is meant to be used when a -- second compiler is used to create the cross-references info. This info -- is put in a separate subdirectory of the object directory function Object_Dir (Project : Project_Type) return GNATCOLL.VFS.Virtual_File; -- Return the object directory for this project. -- This includes the subdirectory if any was set through Set_Object_Subdir. function Artifacts_Dir (Project : Project_Type) return GNATCOLL.VFS.Virtual_File; -- Return the artifacts directory for this project. This directory should -- be used by various tools to create temporary files and other artifacts. -- -- First check if IDE'Artifacts_Dir is declared and return its value. This -- step is only performed if IDE_mode has been set to True when loading -- the project. -- Otherwise return the object directory if it is set implicitly or -- explicitly. -- Otherwise return project directory, if it is writable. -- If all of the above fails return No_File. -- -- Subdir name specified by Set_Object_Subdir applies both to the object -- and project directory for this query. function Object_Path (Project : Project_Type; Recursive : Boolean := False; Including_Libraries : Boolean := False; Xrefs_Dirs : Boolean := False; Exclude_Externally : Boolean := False) return GNATCOLL.VFS.File_Array; -- Return the object path for this project. The empty string is returned -- if the project doesn't have any object directory (i.e. the user -- explicitly set it to the empty string). If Including_Libraries is -- True and Project is a library project, it returns both object and ALI -- paths (in that order) or only ALI path if project doesn't have object -- directory. If Exclude_Externall is True and Project is externally built -- library project, it returns empty path. -- If an Xrefs Subdir is set in the project to a non-empty -- string, and Xrefs_Dir is set, then the corresponding subdirectory is -- returned if it exists. Else, the subdir corresponding to the current -- builder mode is returned. -- If Recursive is True, it also includes the object path (and ALI paths if -- requested) for all imported projects. -- -- If the view is not fully recomputed, an empty path is returned. function Executables_Directory (Project : Project_Type) return GNATCOLL.VFS.Virtual_File; -- Return the directory that contains the executables generated for the -- main programs in Project. This is either Exec_Dir or Object_Dir. function Library_Directory (Project : Project_Type) return GNATCOLL.VFS.Virtual_File; -- If a library project, return the directory where the library resides. function Library_Ali_Directory (Project : Project_Type) return GNATCOLL.VFS.Virtual_File; -- If a library project, return where the ALI files are copied. --------------- -- File info -- --------------- type Unit_Parts is (Unit_Body, Unit_Spec, Unit_Separate); -- A unit is usually composed of two parts: the spec and the body. -- - Unit_Spec represents package/subprogram/generic declarations -- - Unit_Body represents package/subprogram/generic bodies and subunits. -- - Unit_Separate is used for additional implementation code in Ada -- separates. type File_Info_Abstract is abstract tagged null record; function Less (L, R : File_Info_Abstract'Class) return Boolean; function "<" (L, R : File_Info_Abstract) return Boolean is abstract; type File_Info is new File_Info_Abstract with private; type File_Info_Access is access File_Info; function "<" (L, R : File_Info) return Boolean; -- Various information that can be gathered about a file procedure Free (Self : in out File_Info_Access); -- Free the memory used by Self function Project (Info : File_Info'Class; Root_If_Not_Found : Boolean := False) return Project_Type; -- Retrieve the project that the file belongs to. If the file is not a -- source of the project, No_Project is returned, unless Root_If_Not_Found -- is true, in which case the root project is returned. function Unit_Part (Info : File_Info'Class) return Unit_Parts; function Unit_Name (Info : File_Info'Class) return String; function Language (Info : File_Info'Class) return String; function File (Info : File_Info'Class) return GNATCOLL.VFS.Virtual_File; -- Retrieve information about the file. function Other_File (Self : Project_Tree; File : GNATCOLL.VFS.Virtual_File) return GNATCOLL.VFS.Virtual_File; -- If Info is a spec, returns the body of the same unit. If Info is a -- body, returns its spec. -- If there is no "other file" in the project, but we could compute the -- name it should have, that name is returned (the file is created in the -- same directory as File). -- Otherwise, File itself is returned. function Info (Self : Project_Tree'Class; File : GNATCOLL.VFS.Virtual_File) return File_Info; pragma Precondition (not Self.Root_Project.Is_Aggregate_Project); -- Retrieve information about the source file. -- The language is computed from the project's naming scheme and from the -- additional extensions registered through Add_Language_Extension. -- Can only be applied if root project is not an aggregate project, -- Program_Error raised otherwise. package File_Info_Sets is new Ada.Containers.Indefinite_Ordered_Sets (File_Info_Abstract'Class, Less); type File_Info_Set is new File_Info_Sets.Set with null record; function Info_Set (Self : Project_Tree'Class; File : GNATCOLL.VFS.Virtual_File) return File_Info_Set; -- Retrieve information about the source file. -- The language is computed from the project's naming scheme and from the -- additional extensions registered through Add_Language_Extension. -- Can be applied both to aggregate and regular projects. For aggregate -- project tree may return several elements in the set. -- -- This function never returns an empty set. When the file does not belong -- to the project, the function returns a set with a single element. In -- this element, the project field is set to No_Project, but other fields -- are set to best guesses (like the language of the file for instance). ----------- -- Files -- ----------- function Source_Files (Project : Project_Type; Recursive : Boolean := False; Include_Externally_Built : Boolean := True) return GNATCOLL.VFS.File_Array_Access; -- Return the list of source files belonging to the project. The list is -- alphabetically sorted by the full paths of the files. -- If Recursive is False, only the direct sources of the project are -- returned. Otherwise, the sources from imported projects are returned as -- well. -- -- The returned value must be freed by the user -- -- The sources that are returned are not necessarily the ones that are used -- when compiling the root project, since some of them might be overridden -- by extending projects. Instead, they are the sources that would be used -- when compiling from Project ("gnatmake -PProject"). Base names of -- returned files may not be unique in case when root project is an -- aggregate project. For languages other than Ada multiple sources with -- same base name can also be returned. -- If Include_Externally_Built is False then source directories belonging -- to project marked "Externally_Built" will not be returned. function Extended_Projects_Source_Files (Project : Project_Type) return GNATCOLL.VFS.File_Array_Access; -- Returns sources of the given project and of all projects it may possibly -- extend. type File_And_Project is record File : GNATCOLL.VFS.Virtual_File; Project : GNATCOLL.Projects.Project_Type; end record; type File_And_Project_Array is array (Natural range <>) of File_And_Project; type File_And_Project_Array_Access is access all File_And_Project_Array; procedure Free (Self : in out File_And_Project_Array_Access); -- Free the memory used by Self function Source_Files (Project : Project_Type; Recursive : Boolean := False; Include_Project_Files : Boolean := False) return File_And_Project_Array_Access; -- Return the list of source files (recursively) for Project. -- For each file, include the name of its project, which is especially -- useful in the context of aggregate projects. -- If Include_Project_Files is true, then the .gpr files themselves will -- be included in the result -- Result must be freed by the caller. function Direct_Sources_Count (Project : Project_Type) return Natural; -- Return the number of direct source files for Project function Create (Self : Project_Tree; Name : GNATCOLL.VFS.Filesystem_String; Project : Project_Type'Class := No_Project; Use_Source_Path : Boolean := True; Use_Object_Path : Boolean := True) return GNATCOLL.VFS.Virtual_File; procedure Create (Self : Project_Tree; Name : GNATCOLL.VFS.Filesystem_String; Project : Project_Type'Class := No_Project; Use_Source_Path : Boolean := True; Use_Object_Path : Boolean := True; Ambiguous : out Boolean; File : out GNATCOLL.VFS.Virtual_File; Predefined_Only : Boolean := False); -- Create a new file. This will automatically try to solve Name to an -- absolute path if it currently is a base name. -- -- If Name is an absolute path, it is returned as is. Otherwise, only the -- base name is used (i.e. we remove any directory information from Name). -- -- If a source file matches Name and Use_Source_Path is true, it is always -- returned, whether it is part of Project or not. This is the most -- frequent use for this function. We never look at the cache when a -- specific project is specified, since you might be looking for sources -- that are in fact overridden in an extending project. -- Set Predefined_Only to True to disable looking in the project sources -- and only look in the predefined source files. -- -- Otherwise, the file will be searched for in the source dirs and/or -- object dirs of either a specific Project or in the whole project tree. -- The result is cached for efficiency. -- As a special case, if Name ends with '.gpr', it is also looked for among -- the already loaded project, even if their directory is outside the -- source dirs and object dirs. See also Project_From_Name. -- -- If no such file is found, GNATCOLL.VFS.No_File is returned and -- Ambiguous is set to False. -- -- The matching from base source names to full path names is potentially -- ambiguous when using aggregate projects, because it is valid to have -- multiple files with the same base name within a given project tree. -- In such an ambiguous case, this function will return No_File. -- To lift this ambiguity, and if you know which project the file is found -- in, you must pass a Project argument. The file must be a direct source -- of that project. -- -- If a given full path is part of the sources for several projects, this -- is also considered as ambiguous, because the associated object file, -- for instance, is different. However, in this case the returned value is -- set to the common source file, and Ambiguous is set to True. -- -- When a file is ambiguous, No_File is returned, and Ambiguous (if given) -- is set To True. -- -- If you are not sure which project the file belongs to, you can also use -- Create_From_Project below. function Create_From_Project (Self : Project_Type'Class; Name : GNATCOLL.VFS.Filesystem_String) return File_Info; pragma Precondition (Project_Type (Self) = No_Project or else not Self.Is_Aggregate_Project); -- This is similar to Create above (converts from a base name to a full -- path for a source file). -- Here, however, the source is searched in the specified project or -- any of the projects it imports (Create only searches in the direct -- sources of the project). This function also only works for source files, -- not for project files or ALI files. -- This function will also search in the predefined source path. -- Self must not be an aggregate project, to remove ambiguities. function Predefined_Source_Files (Self : access Project_Environment) return GNATCOLL.VFS.File_Array; -- Return the list of sources found in the predefined directories (e.g. the -- Ada runtime). -- Computing this information will take long the first time function Has_Multi_Unit_Sources (Project : Project_Type) return Boolean; -- Whether at least one source file from the project contains multiple -- units (language is unspecified, but will in general be Ada since that's -- currently the only unit-based language supported by project files). function Executable_Name (Project : Project_Type; File : GNATCOLL.VFS.Filesystem_String; Include_Suffix : Boolean := False) return GNATCOLL.VFS.Filesystem_String; -- Return the name of the executable, either read from the project or -- computed from File. This name does not include executable suffixes (like -- ".exe" for instance) unless Include_Suffix is set to True. -- If Project is No_Project, the default executable name for File is -- returned. function Is_Main_File (Project : Project_Type; File : GNATCOLL.VFS.Filesystem_String; Case_Sensitive : Boolean := True) return Boolean; -- Return True if File is one of the main files of Project. -- If File is an absolute path, additionally checks if it is a source of -- Project, otherwise just the base name is used to compare against the -- list of Main units specified in the project. -- Case_Sensitive indicates whether the build machine is case sensitive. -- In general, this machine is the local machine on which the application -- is running, but sometimes you might actually want to process the project -- on a remote server. -- You need to specify the sensitivity of the remote server. type Status_Type is (Success, Incomplete_Closure, Error); procedure Get_Closures (Project : Project_Type; Mains : GNATCOLL.VFS.File_Array_Access; All_Projects : Boolean := True; Include_Externally_Built : Boolean := False; Status : out Status_Type; Result : out GNATCOLL.VFS.File_Array_Access); -- Return the list of source files in the closures of the Ada Mains in -- Result. -- The project and its project tree must have been parsed and processed. -- Mains is a list of single file names that are Ada sources of the project -- Project or of its subprojects. -- When All_Projects is False, the Mains must be sources of the Project and -- the sources of the closures that are sources of the imported subprojects -- are not included in the returned list. -- When All_Projects is True, mains may also be found in subprojects, -- including aggregated projects when Project is an aggregate project. -- When All_Projects is True, sources in the closures that are sources of -- externally built subprojects are included in the returned list only when -- Include_Externally_Built is True. -- Result is the list of path names in the closures. -- It is the responsibility of the caller to free Result. -- When all the sources in the closures are found, Result is non null and -- Status is Success. -- When only a subset of the sources in the closures are found, Result is -- non null and Status is Incomplete_Closure. -- When there are other problems, Result is null and Status is Error. function Library_Files (Self : Project_Type; Recursive : Boolean := False; Including_Libraries : Boolean := True; Xrefs_Dirs : Boolean := False; ALI_Ext : GNATCOLL.VFS.Filesystem_String := ".ali"; Include_Predefined : Boolean := False; Exclude_Overridden : Boolean := True) return GNATCOLL.VFS.File_Array_Access; -- Return a list of all LI files for this project. This never returns null. -- The parameters are similar to that of Object_Path. -- -- If Recursive is True, all LI files from Self or the projects it imports -- are returned. -- If Recursive is False, and Self is an extended project, no LI file is -- ever returned. If Self is not an extended project, then all its LI files -- and the ones from the projects it extends are returned. This behavior is -- such that the LI files logically belongs to the extending project. -- -- ALI_Ext is the suffix to use for those files. As a special case, if -- it starts with "^" it is considered as a regexp matching the basename of -- relevant files. -- -- Including_Libraries controls whether the project's Library_Dir is -- taken into account. This has the following impacts: -- * if True: when a project only has a library_dir (for instance a -- third party library with Externally_Built set to "true"), then the -- ALI files are read in that directory. When a library project has -- both an object_dir and a library_dir, then only the former is -- searched, and the library_dir is ignored (since the object files -- are copied from object_dir to library_dir by the builder). -- * if False, then library_dir is always ignored. As such, a third -- party library project will have no ALI file. -- ??? In general, passing False is of little interest since some ALI -- files will be missing. -- If Include_Predefined is True, then the predefined object directories -- (generally the Ada runtime for instance) will also be searched. Setting -- this to True probably only makes sense when Recursive is also True, -- although this isn't enforced. -- -- If Exclude_Overridden is true, then the files that also exist in an -- extending project are not included in the result. For instance, the -- extending project might also have a "pkg.ali" if "pkg.ads" was -- recompiled in the context of the extending project, and thus we do not -- need to look at "pkg.ali" from the extended project. type Library_Info is record Library_File : GNATCOLL.VFS.Virtual_File; LI_Project : Project_Type_Access; Non_Aggregate_Root_Project : Project_Type_Access; Source : File_Info_Access; end record; -- Source is set to null for ALI files found in the predefined source -- path, since we do not know the mapping to source files in this context. -- When is Source *not* set to null??? and what does it correspond to -- in that case??? -- -- LI_Project is the project in which the LI file was found. It might not -- be the same as the source's project, when using extending projects. -- null for predefined sources. -- -- Non_Aggregate_Root_Project is the non-aggregated root project for the -- tree. When using aggregated projects, it will take the value of any of -- the aggregated project. In other cases, this is the project loaded by -- the user. Set to null for predefined sources. procedure Free (Self : in out Library_Info); -- Free the memory used by Self package Library_Info_Lists is new Ada.Containers.Doubly_Linked_Lists (Library_Info); type Library_Info_List is new Library_Info_Lists.List with null record; overriding procedure Clear (Self : in out Library_Info_List); procedure Library_Files (Self : Project_Type; Recursive : Boolean := False; Including_Libraries : Boolean := True; Xrefs_Dirs : Boolean := False; ALI_Ext : GNATCOLL.VFS.Filesystem_String := ".ali"; Include_Predefined : Boolean := False; List : in out Library_Info_List'Class; Exclude_Overridden : Boolean := True); -- same as Library_Files, but also returns information about the source -- file associated with each LI file. -- The new files are appended to the list, as a way to collect multiple -- extensions (in addition to the support of regexp for ALI_Ext). ------------------ -- Config files -- ------------------ procedure Set_Save_Config_File (Self : in out Project_Environment; Name : GNATCOLL.VFS.Filesystem_String); -- If Name is not No_File, then the configuration file that is used to -- parse the project will be saved to the root project's object dir (or -- if there is none to the same directory as the root project), as Name. -- This config file is the one set via Set_Config_File below, possibly -- modified by adding the custom naming schemes created via -- Register_Default_Language_Extension. -- Such a project file can then be passed to other project-aware tools, -- and they won't have to call Register_Default_Language_Extension. procedure Set_Config_File (Self : in out Project_Environment; Config_File : GNATCOLL.VFS.Virtual_File); -- Set the name of a configuration file to parse before loading the -- project. Such a file is in general generated when running 'gprconfig' -- on the command line, and will contain the default naming schemes (among -- other information) used for all projects. -- If the file does not exist, it will be created automatically if -- you also call Set_Automatic_Config_File. -- All the attributes defined in that file will provide the default value -- when loading projects later on. function Get_Config_File (Self : Project_Environment) return GNATCOLL.VFS.Virtual_File; pragma Inline (Get_Config_File); -- Return current configuration file procedure Set_Automatic_Config_File (Self : in out Project_Environment; Autoconf : Boolean := True); -- Whether this package should spawn 'gprconfig' to generate a -- configuration file automatically. -- If a name was specified via Set_Config_File and the file exists, it is -- parsed (and not regenerated). -- The switch --target will be passed to gprconfig only if the project -- defines the Target attribute or Set_Target_And_Runtime was called. -- The target is NOT automatically extracted from IDE attributes -- (since their values are not yet known when gprconfig is spawned). function Get_Automatic_Config_File (Self : Project_Environment) return Boolean; pragma Inline (Get_Automatic_Config_File); -- Return Autoconf parameter procedure Add_Config_Dir (Self : in out Project_Environment; Directory : GNATCOLL.VFS.Virtual_File); -- Add a new directory to be searched by gprconfig (when using -- Set_Automatic_Config_File) for XML files that will be used to generate -- the configuration file. procedure Set_Target_And_Runtime_From_Config (Self : in out Project_Environment); -- Override the Runtime and Target attributes with values from the -- configuration file proveded by Set_Config_File. Also takes into account -- the toolchain from the configuration project. -- If the configuration file is not set or doesn't exist of if any errors -- happen during parsing of the config file the environment stays intact. -- This procedure is called during Load so generally there is no need to -- call it explicitly before loading the project. -------------------- -- Naming schemes -- -------------------- -- Through the naming scheme defined in a project, there are several -- information that can be computed: the type of source (implementation or -- specification), the name of the unit (in the case of Ada) or the -- programming language in which the file is written. procedure Register_Default_Language_Extension (Self : in out Project_Environment; Language_Name : String; Default_Spec_Suffix : String; Default_Body_Suffix : String; Obj_Suffix : String := ".o"); -- Register Default_Spec_Suffix and Default_Body_Suffix as the default -- extensions for the language. This procedure impacts the loading of -- projects (in particular the automatic search for source files in the -- source directories), so should be called before loading the project. -- Language_Name is case-insensitive. -- The two suffixes also become the default value returned when you -- query the value of the Spec_Suffix_Attribute or Impl_Suffix_Attribute -- for a project that does not explicit define them. -- The Obj_Suffix should be set to "-" or "" for languages that do not have -- object files (XML, txt,...) so that Library_Files does not try to -- match a .ali or .o file to the corresponding source. For Ada and -- C, the obj_suffix should be set to ".o". procedure Add_Language_Extension (Self : in out Project_Environment; Language_Name : String; Extension : String); -- Register Extension (which should include '.') as a valid extension for -- the language. This is used by Get_File_Info. -- Language_Name is case-insensitive. -- This procedure is meant to be called if you need more extensions than -- the ones provided by Register_Default_Language_Extension, or if the -- notion of spec/body does not apply to this specific language. function Registered_Extensions (Self : Project_Environment; Language_Name : String) return GNAT.Strings.String_List; -- Return the list of registered extensions for Language_Name. -- The returned value must be freed by the user. Language_Name is -- case-insensitive. function File_From_Unit (Project : Project_Type; Unit_Name : String; Part : Unit_Parts; Language : String; File_Must_Exist : Boolean := True) return GNATCOLL.VFS.Filesystem_String; -- Return the base name for the given unit. The empty string is -- returned if this unit doesn't belong to the project, or if the concept -- of unit doesn't apply to the language. If File_Must_Exist is False, then -- the name of the file that would be used is returned, even if no such -- file currently exists in the project. -- -- If Project is No_Project, the default naming scheme is used ------------------------ -- Accessing projects -- ------------------------ function Root_Project (Self : Project_Tree'Class) return Project_Type; -- Returns the root project of the tree. From this project, all other -- projects can be reached through "with" or "limited with". This is the -- project that the user initially loaded through Load. function Project_From_Name (Self : Project_Tree'Class; Name : String) return Project_Type; -- Select a project by name. -- When using aggregate projects, there could be multiple projects with the -- same name. In this case, No_Project is returned. function Project_From_Path (Self : Project_Tree'Class; Path : GNATCOLL.VFS.Virtual_File) return Project_Type; -- Select a project by path type Inner_Project_Iterator is private; type Project_Iterator is private; -- Iterate over projects in a tree. -- There is no need to free such an iterator. -- Example of use: -- Iter : Project_Iterator := Start (Tree.Root_Project); -- loop -- Project := Current (Iter); -- exit when Project = No_Project; -- ... -- Next (Iter); -- end loop; -- As opposed to a Project_Iterator, this one does not return aggregated -- projects. function Start (Root_Project : Project_Type; Recursive : Boolean := True; Direct_Only : Boolean := False; Include_Extended : Boolean := True) return Project_Iterator; pragma Precondition (Root_Project /= No_Project); -- Initialize the iterator to start at Root_Project. -- It will process Root_Project and all its subprojects, recursively, but -- without processing the same project twice. -- -- The project nodes are returned sorted topologically (i.e. first the -- projects that don't depend on anything, then their parents, and so on -- until the root project). Extended projects are always returned before -- their extending project. -- -- If Recursive is False, then the only project ever returned is -- Root_Project. This is provided only to simplify the caller's code -- -- The projects extended by Root_Project, if any, are also returned if -- Include_Extended is true and if Direct_Only is False. -- -- If Direct_Only is True and Recursive is True, then only the projects -- that are imported directly by Root_Project are returned. -- -- Projects mentioned in a Project_Files attribute (aggregate project -- or library aggregate project) will also be returned (and their own -- dependencies recursively, if needed). -- -- Start should not be called before the view has been fully recomputed. function Start_Reversed (Root_Project : Project_Type; Recursive : Boolean := True; Direct_Only : Boolean := False; Include_Extended : Boolean := True) return Project_Iterator; -- Same as above, but returns the project in the reverse order, thus: -- root_project, project, project_extended_by_project function Current (Iterator : Project_Iterator) return Project_Type; -- Return the project currently pointed to by the iterator. -- No_Project is returned if there are no more projects to process. procedure Next (Iterator : in out Project_Iterator); -- Move to the next imported project function Is_Limited_With (Iterator : Project_Iterator) return Boolean; -- Return true if the current project is imported directly and through a -- "limited with" clause. False otherwise. function Find_All_Projects_Importing (Project : Project_Type; Include_Self : Boolean := False; Direct_Only : Boolean := False) return Project_Iterator; -- Return the list of all the projects that import Project, either directly -- or indirectly. It also includes projects that extend Project, and their -- own extensions, so that a project and all its extensions are considered -- as the same project. Aggregate library projects are also included in the -- list, if Project or one of the projects importing it is aggregated by -- the aggregate library. Aggregate projects (not libraries) are not added -- to the list. -- If Project is No_Project, the resulting iterator returns all the -- projects in the hierarchy. -- If Include_Self is true, then Project will be included in the iterator -- (if it isn't No_Project, of course). -- If Direct_Only is true, then only the projects that directly import -- Project are returned function Has_Imported_Projects (Project : Project_Type) return Boolean; -- Return True if Project has at least one directly imported project procedure Project_Imports (Parent : Project_Type; Child : Project_Type'Class; Include_Extended : Boolean := False; Imports : out Boolean; Is_Limited_With : out Boolean); -- Return True if Parent imports directly Child. -- Is_Limited_With is set to true if the parent imports child through a -- "limited with" clause -- if Parents or Child is No_Project, True is returned. -- If Include_Extended is true, then True is also returned if Child is an -- extended project of Parent -- If Parent is an aggregate library and Child is one of it's aggregated -- projects, True is returned. type Project_Array is array (Positive range <>) of aliased Project_Type; type Project_Array_Access is access all Project_Array; procedure Unchecked_Free (Arr : in out Project_Array_Access); Empty_Project_Array : constant Project_Array; function Aggregated_Projects (Project : Project_Type; Unwind_Aggregated : Boolean := True) return Project_Array_Access; -- Return the list of projects aggregated by Project. If Unwind_Aggregated -- is True then any aggregated projects that are aggregate projects -- themselves are also resolved into their aggregated projects recursively. -- For non-aggregate projects returns empty list. -- Result must be freed by the caller. --------------- -- Scenarios -- --------------- -- The view of a project is potentially impacted by the value of special -- variables that take their value from the environment. Such variables are -- called scenario variables. Typically, the list of source files and -- switches will be different in various scenarios, although most aspects -- of a project can be changed that way. -- -- Such a variable is typically written as follows in gpr files: -- type Build_Type is ("Debug", "Production"); -- Build : Build_Type := external ("BUILD"); -- where "BUILD" is the external_name, "Debug" and "Production" are the -- possible values. -- If however you have a variable declared as: -- type Build2_Type is ("Debug_Mode", "Production_Mode"); -- Build2 : Build2_Type := external ("BUILD2") & "_Mode"; -- then BUILD2 is not considered as a scenario variable: it is not -- possible in the general case to find the set of valid values for -- instance. -- The same goes to composite default values: -- type Build2_Type is ("Debug_Mode", "Production_Mode"); -- Build2 : Build2_Type := external ("BUILD2", "Production" & "_Mode"); -- -- All other project variables like untyped externals or the concatenation -- case described above are considered Untyped Variables and have a lesser -- range of manipulation, basically get and set are available for them. -- -- For the latest case (composite default) only Set_Value is available -- once the project is loaded, both External_Default and Value will return -- an empty string, although the project will be loaded normally. Once -- the value of such external is changed by Set_Value, Value will return -- proper values. type Scenario_Variable is private; type Scenario_Variable_Array is array (Natural range <>) of aliased Scenario_Variable; type Scenario_Variable_Array_Access is access Scenario_Variable_Array; No_Variable : aliased constant Scenario_Variable; All_Scenarios : aliased constant Scenario_Variable_Array; type Untyped_Variable is private; type Untyped_Variable_Array is array (Natural range <>) of aliased Untyped_Variable; type Untyped_Variable_Array_Access is access Untyped_Variable_Array; No_Untyped_Variable : aliased constant Untyped_Variable; Empty_Untyped_Variable_Array : aliased constant Untyped_Variable_Array; function Scenario_Variables (Self : Project_Tree; Root_Only : Boolean := False) return Scenario_Variable_Array; -- Return the list of scenario variables used in the whole project -- tree, unless Root_Only is set to True. In the latter case only -- variables declared in the root project are returned. -- The result of whole tree computation is cached for efficiency. -- Two variables are considered the same if they reference the same -- environment variable. The reason is that they might not have the same -- name internally in imported projects, however, they will always have the -- same value. -- The variables stored in the result have the value they had when the -- project was loaded. function Untyped_Variables (Self : Project_Tree; Root_Only : Boolean := False) return Untyped_Variable_Array; -- Return the list of scenario variables used in the whole project -- tree, unless Root_Only is set to True. In the latter case only -- variables declared in the root project are returned. function Scenario_Variables (Self : Project_Tree; External_Name : String; Root_Only : Boolean := False) return Scenario_Variable; -- Return the scenario variable associated with External_Name. -- If you call Value on the result, you get the current value it had when -- the project was loaded. -- If the project does not contain such a variable (for instance because -- you call this function before loading the project), a new variable is -- created. -- If Root_Only is set to True and the root project does not have such -- a variable (even if it is declared in the project tree in some other -- project), No_Variable is returned. function Get_Untyped_Variable (Self : Project_Tree; External_Name : String; Root_Only : Boolean := False) return Untyped_Variable; -- Return the scenario variable associated with External_Name. -- If you call Value on the result, you get the current value it had when -- the project was loaded. -- If the project does not contain such a variable (for instance because -- you call this function before loading the project), a new variable is -- created. -- If Root_Only is set to True and the root project does not have such -- a variable (even if it is declared in the project tree in some other -- project), No_Untyped_Variable is returned. function External_Name (Var : Scenario_Variable) return String; function External_Name (Var : Untyped_Variable) return String; -- Returns the name of the external variable referenced by Var. -- Empty string is returned if Var doesn't reference an external variable. function Possible_Values_Of (Self : Project_Tree; Var : Scenario_Variable) return GNAT.Strings.String_List; -- Return all the possible values for the variable given in parameter. -- The output value needs to be freed by the caller, for instance through -- GNATCOLL.Utils.Free function External_Default (Var : Scenario_Variable) return String; function External_Default (Var : Untyped_Variable) return String; -- Return the default value for the external variable, computed for the -- current view of the project. procedure Set_Value (Var : in out Scenario_Variable; Value : String); procedure Set_Value (Var : in out Untyped_Variable; Value : String); -- Change the value stored in Var. -- This does not affect the environment or the loaded project. In general, -- you would use it as: -- Vars : Scenario_Variable_Array := Tree.Scenario_Variables; -- Set_Value (Vars (Vars'First), "new_value"); -- Set_Value (Vars (Vars'First + 1), "new_value2"); -- Tree.Change_Environment (Vars); -- Tree.Recompute_View; -- Instead of calling Change_Environment, you could also use Vars in calls -- to Set_Attribute_Value for instance. -- This procedure does not check that the value is valid for this -- variable. procedure Change_Environment (Self : Project_Tree; Vars : Scenario_Variable_Array; UVars : Untyped_Variable_Array := Empty_Untyped_Variable_Array); procedure Change_Environment (Self : Project_Environment; Name, Value : String); -- Change the environment value for all the variables in Vars (you do not -- need to have all the scenario variables from the project, only those -- you are interested to change). These values will be used when -- Recompute_View is called (which you should do). -- The second version (which applies to the environment) can be used before -- a project is loaded. It will not impact already loaded projects. function Value (Var : Scenario_Variable) return String; function Value (Var : Untyped_Variable) return String; -- Return the values set for Var. -- This value is not necessary that when the project was loaded, if you -- have used Set_Value. However, it will be if the variable comes straight -- from the result of Tree.Scenario_Variables. function Value (Self : Project_Environment; Name : String) return String; -- Return the value that the variable will use when a project is loaded. -- This is different from Value above which reports the value as seen in -- the loaded project, but is only valid once a project has been loaded. --------------- -- Languages -- --------------- function Languages (Project : Project_Type; Recursive : Boolean := False) return GNAT.Strings.String_List; -- Return the value of the Languages attribute. You should use this -- function instead of Get_Attribute_Value, since it will correctly default -- to Ada if no language was defined by the user. -- If Recursive is true, then all the languages supported by Project -- or its imported projects will be returned. -- The list might be empty, if all language attributes in all projects -- were defined to the empty list by the user. -- The returned value must be freed by the user. function Has_Language (Project : Project_Type; Language : String) return Boolean; -- Whether the specified language is used by that project ----------------------- -- Build environment -- ----------------------- function Get_Target (Project : Project_Type; Default_To_Host : Boolean := True) return String; -- Return the target configured in the project, if any, and the empty -- string otherwise. -- If Default_To_Host is set to True and Target is not specified explicitly -- in the project itself or those it extends the host platform is returned -- instead of empty string. function Get_Runtime (Project : Project_Type) return String; -- Return the runtime configured in the project, if any, and the empty -- string otherwise. This concerns only the runtime for Ada. function Target_Same_As_Host (Project : Project_Type) return Boolean; -- Return true when specified Target is either the same as the host -- or belongs to the set of corresponding fallback targets. -------------- -- Switches -- -------------- procedure Switches (Project : Project_Type; In_Pkg : String; File : GNATCOLL.VFS.Virtual_File; Language : String; Value : out GNAT.Strings.String_List_Access; Is_Default_Value : out Boolean); -- Return the switches to use for a file in a given package (gnatmake, -- compiler, ...). -- Value is the list of switches to use for that variable. The result must -- be freed by the caller (never null). -- Is_Default_Value is set to true if file-specific switches were not -- specified, and Value is in fact the list of default switches defined -- at the package level. -- File can be the empty string if you want to find the default switches to -- use for all files in the project. In that case, this procedure returns -- the switches to use for Language. ------------------------ -- Project attributes -- ------------------------ -- The sections above have sometimes provided convenient accessors for the -- project's attributes. However, not all attributes have dedicated -- getters, and the subprograms in this section provide the necessary API -- to access the value of any attribute. -- To avoid typos, a set of constants is provided for all known attributes -- in a project. -- -- Note, that on some platforms the Index is case-sensitive when it is a -- language name. For example, if the projects has -- for Attribute ("ada") use ... -- and Index is set to "Ada", then False/empty list/No_Project -- will be returned. -- -- It is also not recommended to use attribute related queries to get info -- on runtime and target, since there are legacy ways of specifying those -- not through corresponding attributes but by other means. -- Get_Target and Get_Runtime should be used instead. type Attribute_Pkg_String (<>) is private; type Attribute_Pkg_List (<>) is private; -- The name of attributes, and their type. function Attribute_Project (Project : Project_Type; Attribute : Attribute_Pkg_String; Index : String := "") return Project_Type; -- Returns the project in which the attribute was defined (which, in the -- case of 'renames' declarations might be different from Project). -- Returns No_Project if the attribute is not defined. -- The corresponding attribute would have been set in the returned project -- as: -- for Attribute use "value"; -- or -- for Attribute (Index) use "value"; function Attribute_Value (Project : Project_Type; Attribute : Attribute_Pkg_String; Index : String := ""; Default : String := ""; Use_Extended : Boolean := False) return String; -- Return the value for a string attribute. -- Default is returned if the attribute wasn't set by the user and -- has no default value. -- The corresponding attribute would have been set in the project as: -- for Attribute use "value"; -- or -- for Attribute (Index) use "value"; -- -- If Use_Extended is true and the attribute is not defined in Project -- itself, then the attribute is looked up in the project extended by -- Project (if any). function Attribute_Value (Project : Project_Type; Attribute : Attribute_Pkg_List; Index : String := ""; Use_Extended : Boolean := False) return GNAT.Strings.String_List_Access; -- Same as above, but for an attribute whose value is a list. -- -- The returned value is the one read in the project, or the default value -- if one is defined by the project manager. If none exist, this function -- could return null. If you need to find out whether the user has -- explicitly defined the attribute in his project, use Has_Attribute -- instead. -- -- It is the responsibility of the caller to free the memory. -- The corresponding attribute would have been set in the project as: -- for Attribute use ("value1", "value2"); -- or -- for Attribute (Index) use ("value1", "value2"); -- -- Note, that on some platforms the Index is case-sensitive when it is a -- language name. So, if the projects has -- for Attribute ("ada") use ... -- and Index is set to "Ada", then False will be returned. function Attribute_Indexes (Project : Project_Type; Attribute : Attribute_Pkg_String; Use_Extended : Boolean := False) return GNAT.Strings.String_List; function Attribute_Indexes (Project : Project_Type; Attribute : Attribute_Pkg_List; Use_Extended : Boolean := False) return GNAT.Strings.String_List; -- Return the list of indices that are in use for this attribute (i.e. the -- set of values that you can use in the call to Attribute_Value such that -- there is a corresponding attribute in the project file). -- The returned value must be freed by the user (see GNATCOLL.Utils.Free). function Has_Attribute (Project : Project_Type; Attribute : Attribute_Pkg_String; Index : String := "") return Boolean; function Has_Attribute (Project : Project_Type; Attribute : Attribute_Pkg_List; Index : String := "") return Boolean; -- True if the attribute was explicitly defined in the project through -- for Attribute (Index) use ... -- or for Attribute use ... function Build (Package_Name, Attribute_Name : String) return Attribute_Pkg_String; function Build (Package_Name, Attribute_Name : String) return Attribute_Pkg_List; -- Build an attribute reference. To get a top-level attribute reference, -- Package_Name should be an empty string. Builder_Package : constant String; Compiler_Package : constant String; Linker_Package : constant String; Binder_Package : constant String; Naming_Package : constant String; Ide_Package : constant String; GNAT_Attribute : constant Attribute_Pkg_String; Gnatlist_Attribute : constant Attribute_Pkg_String; Compiler_Command_Attribute : constant Attribute_Pkg_String; Debugger_Command_Attribute : constant Attribute_Pkg_String; Program_Host_Attribute : constant Attribute_Pkg_String; Protocol_Attribute : constant Attribute_Pkg_String; Library_Name_Attribute : constant Attribute_Pkg_String; VCS_File_Check : constant Attribute_Pkg_String; VCS_Log_Check : constant Attribute_Pkg_String; VCS_Kind_Attribute : constant Attribute_Pkg_String; VCS_Repository_Root : constant Attribute_Pkg_String; VCS_Patch_Root : constant Attribute_Pkg_String; Global_Pragmas_Attribute : constant Attribute_Pkg_String; Local_Pragmas_Attribute : constant Attribute_Pkg_String; Locally_Removed_Files_Attribute : constant Attribute_Pkg_List; Documentation_Dir_Attribute : constant Attribute_Pkg_String; Origin_Project_Attribute : constant Attribute_Pkg_String; Target_Attribute : constant Attribute_Pkg_String; Runtime_Attribute : constant Attribute_Pkg_String; -- Naming package Casing_Attribute : constant Attribute_Pkg_String; Specification_Suffix_Attribute : constant Attribute_Pkg_String; Implementation_Suffix_Attribute : constant Attribute_Pkg_String; Separate_Suffix_Attribute : constant Attribute_Pkg_String; Spec_Suffix_Attribute : constant Attribute_Pkg_String; Impl_Suffix_Attribute : constant Attribute_Pkg_String; Dot_Replacement_Attribute : constant Attribute_Pkg_String; Spec_Attribute : constant Attribute_Pkg_String; Body_Attribute : constant Attribute_Pkg_String; Spec_Exception_Attribute : constant Attribute_Pkg_List; Impl_Exception_Attribute : constant Attribute_Pkg_List; -- The following attributes should be read through specialized subprograms -- (Get_Languages,...) Source_Dirs_Attribute : constant Attribute_Pkg_List; Source_Files_Attribute : constant Attribute_Pkg_List; Source_List_File_Attribute : constant Attribute_Pkg_String; Obj_Dir_Attribute : constant Attribute_Pkg_String; Languages_Attribute : constant Attribute_Pkg_List; Main_Attribute : constant Attribute_Pkg_List; Exec_Dir_Attribute : constant Attribute_Pkg_String; Builder_Default_Switches_Attribute : constant Attribute_Pkg_List; Compiler_Default_Switches_Attribute : constant Attribute_Pkg_List; Linker_Default_Switches_Attribute : constant Attribute_Pkg_List; Binder_Default_Switches_Attribute : constant Attribute_Pkg_List; Executable_Attribute : constant Attribute_Pkg_String; Excluded_Source_Files_Attribute : constant Attribute_Pkg_List; Excluded_Source_List_File_Attribute : constant Attribute_Pkg_String; -- Configuration Compiler_Driver_Attribute : constant Attribute_Pkg_String; -- GNATStack Stack_Switches_Attribute : constant Attribute_Pkg_List; ----------------------- -- Printing projects -- ----------------------- type Pretty_Printer is tagged null record; procedure Put (Self : in out Pretty_Printer; C : Character); -- Output a single character. By default, prints on stdout. procedure Put (Self : in out Pretty_Printer; S : String); procedure New_Line (Self : in out Pretty_Printer); -- Output a string or go to the next line. By default, these are -- implemented by calling Put for a single character procedure Put (Self : in out Pretty_Printer; Project : Project_Type'Class; Increment : Positive := 3; Eliminate_Empty_Case_Constructions : Boolean := False); -- Output a project file, properly formatted. -- By default, all output is done on stdout, although you can change this -- behavior by modifying the primitive operations of the pretty printer. ---------------------- -- Editing projects -- ---------------------- Project_Not_Editable : exception; -- Current implementation does not provide a way to edit aggregate project -- trees. This means that if the root project is an aggregate project then -- neither the root project itself nor any of the projects from aggregated -- project subtrees can be edited. Is_Editable will always return False for -- those, and an attempt to use any of the editing methods will -- result in Project_Not_Editable exception. function Is_Editable (Project : Project_Type) return Boolean; -- Whether the project can be edited. -- This is not the case if it is an aggregate or aggregated project, -- if there were errors loading the project, or if it contains constructs -- that prevent its edition (use of variables for instance). -- Project is also not editable if package IDE contains -- for Read_Only use "true"; -- or file containing the project is write protected. procedure Set_Modified (Project : Project_Type; Modified : Boolean); function Modified (Project : Project_Type; Recursive : Boolean := False) return Boolean; -- Return True if Project has been modified, but not saved. -- If Recursive is True, this function will also return True if one of the -- imported project has been modified. procedure Set_Attribute (Self : Project_Type; Attribute : Attribute_Pkg_List; Values : GNAT.Strings.String_List; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""; Prepend : Boolean := False); procedure Set_Attribute (Self : Project_Type; Attribute : Attribute_Pkg_String; Value : String; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""; At_Index : Natural := 0); -- Update the value of the attribute in the project. -- Values is the list of new values for the attribute. The caller is still -- responsible for freeing the memory when this call finished. -- Index is the index for the attribute (for instance the file name when -- modifying the switches). -- A null entry in Values is ignored. -- This subprogram properly handles renaming packages (i.e the project -- that contains the real definition of the package is modified, not -- necessarily Project itself). -- The change only occurs for the specified scenario, without affecting -- over scenarios. The project might need to be normalized in this case -- (see above). If Scenario is set to All_Scenarios, the change impacts all -- scenarios. -- If Prepend is False, these values are the only values for the -- variable, and they override any other value that was there before. If -- Prepend is True, the values in List are prepended to the current -- value of the attribute. -- You will need to call Recompute_View afterwards. -- -- At_Index is used in some rare cases, and corresponds to the following -- construct in the project file: -- for Specification ("unit") use "file" at 1; Any_Attribute : constant String := "@@"; -- Special value for all the subprograms that take an Attribute_Index -- parameter. When this is used, no matching is done on the indices. procedure Delete_Attribute (Self : Project_Type; Attribute : Attribute_Pkg_String; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""); procedure Delete_Attribute (Self : Project_Type; Attribute : Attribute_Pkg_List; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""); -- Remove all declarations for the attribute in the specified -- scenario. This effectively reverses to the default behavior for the -- attribute. -- If Index is Any_Attribute, then this subprogram will not try -- to match the index, and all declarations, whatever the index, will be -- removed. The default index ("") will never match attributes that do have -- an index. -- You will need to call Recompute_View afterwards. function Save (Project : Project_Type; Force : Boolean := False; Errors : Error_Report := null) return Boolean; -- Save the project on the disk. -- If Force is True, then the project is saved even if it isn't modified. -- Return whether the project has been saved (False if there was nothing to -- do or an error. function Create_Project (Tree : Project_Tree'Class; Name : String; Path : GNATCOLL.VFS.Virtual_File) return Project_Type; -- Create a new empty project. -- This project does not replace the one currently loaded in the tree, -- although it becomes available for instance through calls to -- Project_From_Name. procedure Rename_And_Move (Self : Project_Type; New_Name : String; Directory : GNATCOLL.VFS.Virtual_File; Errors : Error_Report := null); -- Rename Project to New_Name. All the nodes in the project tree that -- reference Project, are also updated accordingly. -- Also sets the directory of the project file (the project itself is not -- automatically saved into that directory, you need an explicit Save). -- -- The paths internal to the project are not upgraded, and will remain -- relative paths if they were. -- -- If there is already a project by that name in the project hierarchy, an -- error is reported through Errors. -- -- You will need to call Recompute_View afterwards. procedure Remove_Imported_Project (Project : Project_Type; Imported_Project : Project_Type); -- Remove a dependency from Project. -- If Imported_Project is not already a dependency, then this subprogram -- does nothing. -- You will need to call Recompute_View afterwards. type Import_Project_Error is (Success, Project_Already_Exists, Imported_Project_Not_Found, Dependency_On_Self, Dependency_Already_Exists, Circular_Dependency ); function Add_Imported_Project (Tree : Project_Tree; Project : Project_Type'Class; Imported_Project_Location : GNATCOLL.VFS.Virtual_File; Packages_To_Check : GNAT.Strings.String_List_Access := No_Packs; Errors : Error_Report := null; Use_Relative_Path : Boolean := True; Use_Base_Name : Boolean := False; Limited_With : Boolean := False) return Import_Project_Error; -- Add a new with_statement for Imported_Project. -- Errors while parsing the project file are sent to Report_Errors. -- True is returned if the project was modified with success -- If Use_Base_Name is true then only the base name of the project is used -- in the with statement. Otherwise, if Use_Relative_Path is True, then a -- relative path is used in the with statement, otherwise an absolute path -- is used. -- You will need to call Recompute_View afterwards. -- -- Doesn't work if Tree.Root_Project is an aggregate project. function Add_Imported_Project (Project : Project_Type; Imported_Project : Project_Type; Errors : Error_Report := null; Use_Relative_Path : Boolean := True; Use_Base_Name : Boolean := False; Limited_With : Boolean := False) return Import_Project_Error; -- Same as above, but the project is already in memory function Register_New_Attribute (Name : String; Pkg : String; Is_List : Boolean := False; Indexed : Boolean := False; Case_Sensitive_Index : Boolean := False) return String; -- Register a new attribute that will be allowed in projects. -- This prevents error messages when loading the project. -- Attributes can only be added to packages, not at the top level of a -- project. -- Returns a non-empty string if there is an error creating the attribute function Attribute_Registered (Name : String; Pkg : String) return Boolean; -- Checks is corresponding attribute has already been registered. -- Only applicable for attributes declared in packages, always returns -- True if package is an empty string since it is not possible to register -- top-level attributes anyway. function Rename_Path (Self : Project_Type; Old_Path : GNATCOLL.VFS.Virtual_File; New_Path : GNATCOLL.VFS.Virtual_File; Use_Relative_Paths : Boolean) return Boolean; -- Replace all instances of Old_Path with New_Path. -- This returns True if all occurrences were successfully replaced, False -- otherwise. -- You will need to call Recompute_View afterwards. procedure Set_Extended_Project (Self : GNATCOLL.Projects.Project_Type; Extended : GNATCOLL.Projects.Project_Type; Extend_All : Boolean := False; Use_Relative_Paths : Boolean := False); -- Set the project that Project extends. If Extend_All is True, then this -- is an "extend all" project. -- You will need to call Recompute_View afterwards. -------------------------------- -- Editing scenario variables -- -------------------------------- -- After calling most of the following routines local variables pointing -- to Scenario Variables become obsolete and need to be reassigned again -- through a call to Scenario_Variables. Otherwise expected updates, -- such as lists of possible values, will not be observed. procedure Delete_Scenario_Variable (Tree : Project_Tree'Class; External_Name : String; Keep_Choice : String; Delete_Direct_References : Boolean := True); -- Remove all scenario variables that reference External_Name. -- All the case constructions where this variable occur are replaced by -- the case item corresponding to Keep_Choice. -- If Delete_Direct_References is True, then all direct references (i.e. -- external() statements in the project file) to External_Name are also -- removed, in addition to the scenario variables that reference it. -- -- You will need to call Recompute_View afterwards. function Create_Scenario_Variable (Project : Project_Type; Name : String; Type_Name : String; External_Name : String) return Scenario_Variable; -- Create a new typed environment variable, referencing External_Name, and -- whose type is Type_Name. The declaration for the type is automatically -- created. procedure Change_External_Name (Tree : Project_Tree'Class; Variable : in out Scenario_Variable; New_Name : String); -- Change the name of the environment variable associated with Variable. procedure Set_Default_Value (Tree : Project_Tree'Class; External_Name : String; Default : String); -- Change the default value for all the scenario variables based on -- External_Name. procedure Rename_Value (Tree : Project_Tree'Class; External_Name : String; Old_Value : String; New_Value : String); -- Rename one of the choices in the list of possible values for the -- scenario variables associated with External_Name. This also changes -- the default value for external references. procedure Remove_Value (Tree : Project_Tree'Class; External_Name : String; Value : String); -- Remove Value_Name from the list of possible values for the scenario -- variables that refer to External_Name. If this is the last possible -- value, then the result is the same as calling Delete_Scenario_Variable. procedure Add_Values (Tree : Project_Tree'Class; Variable : Scenario_Variable; Values : GNAT.Strings.String_List); -- Add some values to the list of possible values for Variable. -- The caller needs to free Values on return. -------------- -- Internal -- -------------- function Start (Root_Project : Project_Type; Recursive : Boolean := True; Direct_Only : Boolean := False; Include_Extended : Boolean := True) return Inner_Project_Iterator; -- Internal version of Start function Start_Reversed (Root_Project : Project_Type; Recursive : Boolean := True; Direct_Only : Boolean := False; Include_Extended : Boolean := True) return Inner_Project_Iterator; -- Internal Version of Start_Reversed procedure Next (Iterator : in out Inner_Project_Iterator); -- Internal version of Next function Current (Iterator : Inner_Project_Iterator) return Project_Type; -- Internal version of Current function Find_All_Projects_Importing (Project : Project_Type; Root_Project : Project_Type; Include_Self : Boolean := False; Direct_Only : Boolean := False) return Inner_Project_Iterator; -- Inner version of Find_All_Projects_Importing function Is_Limited_With (Iterator : Inner_Project_Iterator) return Boolean; -- Inner version of Is_Limited_With procedure Set_Disable_Use_Of_TTY_Process_Descriptor (Self : in out Project_Environment; Disabled : Boolean); -- GNAT.Expect.TTY.TTY_Process_Descriptor are used internally -- to attach pseudo-terminal to processes launched by the package, -- in particular to query the default search paths of the compilers. -- In some cases, however, they might introduce unwanted complexity -- (for instance when running inside a java virtual machine). It is thus -- possible to disable them and fall back to a simpler way to spawn -- the processes. -- Most users should not have to disable this. private All_Packs : constant GNAT.Strings.String_List_Access := null; No_Strings : aliased GNAT.Strings.String_List := (1 .. 0 => null); No_Packs : constant GNAT.Strings.String_List_Access := No_Strings'Access; Project_File_Extension : constant GNATCOLL.VFS.Filesystem_String := GNATCOLL.VFS."+" (GPR.Project_File_Extension); -- The standard extension for a project file (".gpr") type File_Info is new File_Info_Abstract with record File : GNATCOLL.VFS.Virtual_File; Project : Project_Type; Root_Project : Project_Type; Part : Unit_Parts := Unit_Separate; Name : GPR.Name_Id := GPR.No_Name; -- Unit name Lang : GPR.Name_Id := GPR.No_Name; end record; package Extensions_Languages is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => String, -- file extension Element_Type => GPR.Name_Id, Hash => Ada.Strings.Hash, Equivalent_Keys => "=", "=" => GPR."="); -- maps extensions with a language type Naming_Scheme_Record; type Naming_Scheme_Access is access Naming_Scheme_Record; type Naming_Scheme_Record is record Language : GNAT.Strings.String_Access; Default_Spec_Suffix : GNAT.Strings.String_Access; Default_Body_Suffix : GNAT.Strings.String_Access; Obj_Suffix : GNAT.Strings.String_Access; Next : Naming_Scheme_Access; end record; type Project_Environment is tagged record Env : GPR.Tree.Environment; IDE_Mode : Boolean := False; Autoconf : Boolean := False; Config_File : GNATCOLL.VFS.Virtual_File; -- Name of the .cgpr file to parse for the project. Report_Missing_Dirs : Boolean := True; Packages_To_Check : GNAT.Strings.String_List_Access := No_Packs; Forced_Target : GNAT.Strings.String_Access; Forced_Runtime : GNAT.Strings.String_Access; -- force specific values for runtime and target Gnatls : GNAT.Strings.String_Access; -- The gnatls that was run to set the predefined paths (or unset if the -- paths were set manually). Predefined_Object_Path : GNATCOLL.VFS.File_Array_Access; -- Predefined object path for the runtime library Predefined_Source_Path : GNATCOLL.VFS.File_Array_Access; -- Predefined source paths for the runtime library Predefined_Project_Path : GNATCOLL.VFS.File_Array_Access; -- Predefined project path. -- prj-ext.ads does not expect an empty path, ever Predefined_Source_Files : GNATCOLL.VFS.File_Array_Access; -- The list of source files in Predefined_Source_Path Default_Gnatls : GNAT.Strings.String_Access := new String'("gnatls"); -- The default gnatls command to run. Xrefs_Subdir : GNAT.Strings.String_Access; -- Object dirs subdirectory containing the cross-refs Trusted_Mode : Boolean := True; -- Whether we are in trusted mode when recomputing the project view Extensions : Extensions_Languages.Map; -- The additional extensions registered for each language Naming_Schemes : Naming_Scheme_Access; -- The list of default naming schemes for the languages known to GPS Save_Config_File : GNAT.Strings.String_Access; -- See Set_Save_Config_File Scenario_Variables : Scenario_Variable_Array_Access; Untyped_Variables : Untyped_Variable_Array_Access; -- Cached value of the scenario variables and untyped variables. -- This should be accessed only through the function Scenario_Variables, -- since it needs to be initialized first. TTY_Process_Descriptor_Disabled : Boolean := False; -- when TTY_Process_Descriptor are disabled, Process_Descriptor are -- used instead of TTY_Process_Descriptor to workaround incompatibility. -- Known incompatibility: Java IOException found when gnatcoll-projects -- used from Java on Linux through AJIS. -- For more information see Set_Disable_Use_Of_TTY_Process_Descriptor -- procedure comment. end record; type Name_Id_Array is array (Positive range <>) of GPR.Name_Id; type Name_Id_Array_Access is access Name_Id_Array; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Name_Id_Array, Name_Id_Array_Access); -- Still needed for some routines like Get_All_Possible_Values type Path_Name_Id_Array is array (Positive range <>) of GPR.Path_Name_Type; type Path_Name_Id_Array_Access is access Path_Name_Id_Array; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Path_Name_Id_Array, Path_Name_Id_Array_Access); type Path_Name_Array is record Items : Path_Name_Id_Array_Access; Last : Natural := 0; end record; type Project_Tree_Data; type Project_Tree_Data_Access is access Project_Tree_Data; package Basename_To_Info_Cache is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => String, Element_Type => GNATCOLL.VFS.Virtual_File, Hash => Ada.Strings.Hash, Equivalent_Keys => "=", "=" => GNATCOLL.VFS."="); type Basename_To_Info_Cache_Map_Access is access Basename_To_Info_Cache.Map; type Project_Data is tagged record Refcount : Integer := 1; Node : GPR.Project_Node_Id; View : GPR.Project_Id; Imported_Projects : Path_Name_Array; Importing_Projects : Path_Name_Id_Array_Access; -- Sorted list of imported projects (Cache for Project_Iterator). -- Importing_Project always contains the project itself in last -- position. Non_Recursive_Include_Path : GNATCOLL.VFS.File_Array_Access; -- The include path for this project Tree : Project_Tree_Data_Access; Tree_For_Map : Project_Tree_Data_Access; -- Needed so that we can return other projects like imported projects. -- Tree_For_Map is the tree for the root project, which is used to -- retrieve other projects. Tree_For_Map is the same as Tree in the -- case of non-aggregate projects. View_Is_Complete : Boolean := True; -- True if the view for the project was correctly computed. -- Set to True by default, so that a project just created with -- Create_Project can be immediately edited. Files : GNATCOLL.VFS.File_Array_Access; -- The list of source files for this project Uses_Variables : Boolean := False; -- If the project uses variables ("foo := .."), then it cannot be -- edited graphically, since GPS would break it. Normalized : Boolean := False; -- True if the project has been normalized Modified : Boolean := False; -- True if the project has been modified by the user, and not saved -- yet. Base_Name_To_Full_Path : Basename_To_Info_Cache_Map_Access; -- Cache resolving a base name to a file contained in the project tree -- Note: because of the manual memmory management for Project_Type, -- it's not possible for this to be a Basename_To_Info_Cache.Map, since -- Finalize would not get called on it. end record; type Project_Type is new Ada.Finalization.Controlled with record Data : Project_Data_Access; -- This is an access type for several reasons: -- - Parameters do not have to be of type "access Project_Type", just -- Project_Type, which is lighter to write. At the same time, -- modifying the data of the project will impact all instances that -- reference the same project tree. -- - Since the user doesn't know this is an access type, he can not -- attempt to free the data. Memory is fully controlled by this type -- (and the projects registry). end record; overriding procedure Adjust (Self : in out Project_Type); overriding procedure Finalize (Self : in out Project_Type); function Tree_View (P : Project_Type'Class) return GPR.Project_Tree_Ref; function Tree_Tree (P : Project_Type'Class) return GPR.Tree.Project_Node_Tree_Ref; pragma Inline (Tree_View, Tree_Tree); -- Access to the project tree type Project_Tree is tagged record Data : Project_Tree_Data_Access; end record; type Scenario_Variable is record Ext_Name : GPR.Name_Id; Var_Name : GPR.Name_Id; Default : GPR.Name_Id; String_Type : GPR.Project_Node_Id; Tree_Ref : GPR.Project_Node_Tree_Ref; Value : GPR.Name_Id; First_Project_Path : GPR.Path_Name_Type; -- First project in which corresponding controlling external has been -- discovered. Used for diagnostic message when types of other Scenario -- Variables controlled by same external do not match. end record; type Untyped_Variable is record Name : GPR.Name_Id; Default : GPR.Name_Id; Value : GPR.Name_Id; end record; No_Variable : aliased constant Scenario_Variable := (GPR.No_Name, GPR.No_Name, GPR.No_Name, GPR.Empty_Project_Node, null, GPR.No_Name, GPR.No_Path); All_Scenarios : aliased constant Scenario_Variable_Array (1 .. 0) := (others => No_Variable); No_Untyped_Variable : aliased constant Untyped_Variable := (GPR.No_Name, GPR.No_Name, GPR.No_Name); Empty_Untyped_Variable_Array : aliased constant Untyped_Variable_Array := Untyped_Variable_Array'(1 .. 0 => No_Untyped_Variable); type Inner_Project_Iterator is record Root : Project_Type; Current : Integer; Reversed : Boolean; Importing : Boolean := False; -- True if we are looking for importing projects instead of imported -- projects. Include_Extended : Boolean := True; -- True if we should also return extended projects Direct_Only : Boolean := False; -- Relevant only when listing projects importing Root end record; package Project_Lists is new Ada.Containers.Vectors (Positive, Project_Type); use Project_Lists; type Project_Iterator is record Root : Project_Type; Importing : Boolean := False; -- True if we are looking for importing projects instead of imported -- projects. Only used by Is_Limited_With. Project_List : Project_Lists.Vector := Project_Lists.Empty_Vector; Project_Idx : Natural := Project_Lists.No_Index; end record; type Attribute_Pkg_String is new String; type Attribute_Pkg_List is new String; Builder_Package : constant String := "builder"; Compiler_Package : constant String := "compiler"; Linker_Package : constant String := "linker"; Binder_Package : constant String := "binder"; Naming_Package : constant String := "naming"; Ide_Package : constant String := "ide"; Source_Dirs_Attribute : constant Attribute_Pkg_List := "source_dirs"; Source_Files_Attribute : constant Attribute_Pkg_List := "source_files"; Source_List_File_Attribute : constant Attribute_Pkg_String := "source_list_file"; Locally_Removed_Files_Attribute : constant Attribute_Pkg_List := "locally_removed_files"; Excluded_Source_Files_Attribute : constant Attribute_Pkg_List := "excluded_source_files"; Excluded_Source_List_File_Attribute : constant Attribute_Pkg_String := "excluded_source_list_file"; GNAT_Attribute : constant Attribute_Pkg_String := "ide#gnat"; Gnatlist_Attribute : constant Attribute_Pkg_String := "ide#gnatlist"; Compiler_Command_Attribute : constant Attribute_Pkg_String := "ide#compiler_command"; Debugger_Command_Attribute : constant Attribute_Pkg_String := "ide#debugger_command"; Remote_Host_Attribute : constant Attribute_Pkg_String := "ide#remote_host"; Program_Host_Attribute : constant Attribute_Pkg_String := "ide#program_host"; Protocol_Attribute : constant Attribute_Pkg_String := "ide#communication_protocol"; Main_Attribute : constant Attribute_Pkg_List := "main"; Library_Name_Attribute : constant Attribute_Pkg_String := "library_name"; VCS_File_Check : constant Attribute_Pkg_String := "ide#vcs_file_check"; VCS_Log_Check : constant Attribute_Pkg_String := "ide#vcs_log_check"; Obj_Dir_Attribute : constant Attribute_Pkg_String := "object_dir"; VCS_Kind_Attribute : constant Attribute_Pkg_String := "ide#vcs_kind"; VCS_Repository_Root : constant Attribute_Pkg_String := "ide#vcs_repository_root"; VCS_Patch_Root : constant Attribute_Pkg_String := "ide#vcs_patch_root"; Documentation_Dir_Attribute : constant Attribute_Pkg_String := "documentation#documentation_dir"; Origin_Project_Attribute : constant Attribute_Pkg_String := "origin_project"; Target_Attribute : constant Attribute_Pkg_String := "target"; Runtime_Attribute : constant Attribute_Pkg_String := "runtime"; Global_Pragmas_Attribute : constant Attribute_Pkg_String := "builder#global_configuration_pragmas"; Local_Pragmas_Attribute : constant Attribute_Pkg_String := "compiler#local_configuration_pragmas"; Builder_Default_Switches_Attribute : constant Attribute_Pkg_List := "builder#default_switches"; Compiler_Default_Switches_Attribute : constant Attribute_Pkg_List := "compiler#default_switches"; Linker_Default_Switches_Attribute : constant Attribute_Pkg_List := "linker#default_switches"; Binder_Default_Switches_Attribute : constant Attribute_Pkg_List := "binder#default_switches"; Executable_Attribute : constant Attribute_Pkg_String := "builder#executable"; Casing_Attribute : constant Attribute_Pkg_String := "naming#casing"; Specification_Suffix_Attribute : constant Attribute_Pkg_String := "naming#specification_suffix"; Implementation_Suffix_Attribute : constant Attribute_Pkg_String := "naming#implementation_suffix"; Separate_Suffix_Attribute : constant Attribute_Pkg_String := "naming#separate_suffix"; Spec_Suffix_Attribute : constant Attribute_Pkg_String := "naming#spec_suffix"; Impl_Suffix_Attribute : constant Attribute_Pkg_String := "naming#body_suffix"; Dot_Replacement_Attribute : constant Attribute_Pkg_String := "naming#dot_replacement"; Spec_Attribute : constant Attribute_Pkg_String := "naming#spec"; Body_Attribute : constant Attribute_Pkg_String := "naming#body"; -- Configuration Compiler_Driver_Attribute : constant Attribute_Pkg_String := "compiler#driver"; -- GNATStack Stack_Switches_Attribute : constant Attribute_Pkg_List := "stack#switches"; -- For backward compatibility Old_Specification_Attribute : constant Attribute_Pkg_String := "naming#specification"; Old_Implementation_Attribute : constant Attribute_Pkg_String := "naming#implementation"; Spec_Exception_Attribute : constant Attribute_Pkg_List := "naming#specification_exceptions"; Impl_Exception_Attribute : constant Attribute_Pkg_List := "naming#implementation_exceptions"; -- The following attributes should be read through specialized subprograms -- (Get_Languages,...) Languages_Attribute : constant Attribute_Pkg_List := "languages"; Exec_Dir_Attribute : constant Attribute_Pkg_String := "exec_dir"; No_Gnatls : constant String := "#no-gnatls#"; No_Project : aliased constant Project_Type := (Ada.Finalization.Controlled with Data => null); Empty_Project_Array : constant Project_Array := Project_Array'(1 .. 0 => No_Project); function Get_View (Project : Project_Type'Class) return GPR.Project_Id; function Node (Project : Project_Type'Class) return GPR.Project_Node_Id; function Tree (Data : Project_Tree_Data_Access) return GPR.Tree.Project_Node_Tree_Ref; pragma Inline (Node, Tree, Get_View); -- Needed for the support packages for the edition of project files. function Project_From_Name (Tree : Project_Tree_Data_Access; Name : GPR.Name_Id) return Project_Type'Class; -- Internal version of Project_From_Name function Project_From_Path (Tree : Project_Tree_Data_Access; Path_Id : GPR.Path_Name_Type) return Project_Type'Class; -- Internal version of Project_From_Path function Scenario_Variables (Tree : Project_Tree_Data_Access; Root_Only : Boolean := False) return Scenario_Variable_Array; pragma Inline (Scenario_Variables); -- Internal version of Scenario_Variables function Untyped_Variables (Tree : Project_Tree_Data_Access; Root_Only : Boolean := False) return Untyped_Variable_Array; pragma Inline (Untyped_Variables); -- Internal version of Untyped_Variables procedure Reset_All_Caches (Tree : Project_Tree_Data_Access); -- Reset all the caches for imported/importing projects -- for the whole project hierarchy pragma Inline (Name, Project_Path, Has_Attribute); end GNATCOLL.Projects; gnatcoll-core-21.0.0/src/gnatcoll-remote-db.adb0000644000175000017500000000751013661715457021125 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package body GNATCOLL.Remote.Db is Global_Conf : access Remote_Db_Interface'Class := null; --------------------------------- -- Define_Remote_Configuration -- --------------------------------- procedure Define_Remote_Configuration (Config : access Remote_Db_Interface'Class) is begin Global_Conf := Config; end Define_Remote_Configuration; ------------------- -- Is_Configured -- ------------------- function Is_Configured (Nickname : String) return Boolean is begin if Global_Conf = null then raise Invalid_Remote_Configuration; end if; return Global_Conf.Is_Configured (Nickname); end Is_Configured; ------------ -- Server -- ------------ function Get_Server (Nickname : String) return Server_Access is begin if Global_Conf = null then raise Invalid_Remote_Configuration; end if; return Global_Conf.Get_Server (Nickname); end Get_Server; --------------------- -- Nb_Mount_Points -- --------------------- function Nb_Mount_Points (Nickname : String) return Natural is begin if Global_Conf = null then raise Invalid_Remote_Configuration; end if; return Global_Conf.Nb_Mount_Points (Nickname); end Nb_Mount_Points; -------------------------------- -- Get_Mount_Point_Local_Root -- -------------------------------- function Get_Mount_Point_Local_Root (Nickname : String; Index : Natural) return FS_String is begin if Global_Conf = null then raise Invalid_Remote_Configuration; end if; return Global_Conf.Get_Mount_Point_Local_Root (Nickname, Index); end Get_Mount_Point_Local_Root; ------------------------------- -- Get_Mount_Point_Host_Root -- ------------------------------- function Get_Mount_Point_Host_Root (Nickname : String; Index : Natural) return FS_String is begin if Global_Conf = null then raise Invalid_Remote_Configuration; end if; return Global_Conf.Get_Mount_Point_Host_Root (Nickname, Index); end Get_Mount_Point_Host_Root; end GNATCOLL.Remote.Db; gnatcoll-core-21.0.0/src/gnatcoll-storage_pools-headers.adb0000644000175000017500000001400713661715457023537 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2015-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with System.Memory; use System, System.Memory; with Ada.Unchecked_Conversion; package body GNATCOLL.Storage_Pools.Headers is Default_Align : constant Storage_Count := Standard'System_Allocator_Alignment; ------------------ -- Header_Pools -- ------------------ package body Header_Pools is type Header is record Extra : Extra_Header; end record; Extra_Bytes : constant Storage_Offset := (Header'Max_Size_In_Storage_Elements - Header'Object_Size / Storage_Unit); -- If the header is a controlled type, we need to allocate extra size -- for its Previous and Next pointers. This constant computes how -- much such extra size is needed. Header_Size_Bytes : constant Storage_Count := Header'Size / Storage_Unit; Extra_Allocation_Bytes : constant Storage_Count := ((Header_Size_Bytes + Extra_Bytes + Default_Align - 1) / Default_Align) * Default_Align; -- Allocate a multiple of Default_Align bytes, so that the -- alignment of the Element_Type is suitable. function Convert is new Ada.Unchecked_Conversion (System.Address, Header_Access); function Address_Header_Of (Addr : System.Address) return System.Address is (Addr - Extra_Allocation_Bytes); -- Compute the address of the header. -- Do not call with a null pointer. -------------- -- Allocate -- -------------- overriding procedure Allocate (Self : in out Header_Pool; Addr : out System.Address; Size : System.Storage_Elements.Storage_Count; Alignment : System.Storage_Elements.Storage_Count) is -- The compiler requests a size that include the object size -- plus any extra header like bounds or next/previous for -- controlled types. This size also includes a padding to -- ensure that the element will be properly aligned. -- The computation is done in s-stposu.adb, in -- Header_Size_With_Padding. pragma Unreferenced (Self, Alignment); Aligned_Size : constant Storage_Count := -- bytes Size + Extra_Allocation_Bytes; Allocated : constant System.Address := Alloc (size_t (Aligned_Size)); begin Addr := Allocated + Extra_Allocation_Bytes; end Allocate; ---------------- -- Deallocate -- ---------------- overriding procedure Deallocate (Self : in out Header_Pool; Addr : System.Address; Size : System.Storage_Elements.Storage_Count; Alignment : System.Storage_Elements.Storage_Count) is pragma Unreferenced (Self, Alignment, Size); Header : constant System.Address := Address_Header_Of (Addr); begin System.Memory.Free (Header); end Deallocate; ----------- -- Typed -- ----------- package body Typed is function Header_Of (Element : Element_Access) return Header_Access is Finalization_Size : Integer; -- If the element_type is a controlled type, this will -- be the number of extra bytes requested by the compiler in -- calls to Allocate and Deallocate (see the memory layout -- description in the specs). -- -- These extra bytes are automatically added and substracted by -- the compiler when calling Deallocate, but not when calling -- Header_Of so we need to take them into account when looking -- for the our own header. Descriptor_Size : Integer; -- Similarily, for bounds information if the element type is -- an unconstrained array (e.g. String). begin if Element = null then return null; end if; Finalization_Size := Element.all'Finalization_Size; Descriptor_Size := Element_Type'Descriptor_Size / Storage_Unit; return Convert (Address_Header_Of (Element.all'Address - Storage_Offset (Finalization_Size + Descriptor_Size))); end Header_Of; end Typed; end Header_Pools; end GNATCOLL.Storage_Pools.Headers; gnatcoll-core-21.0.0/src/gnatcoll-projects-krunch.adb0000644000175000017500000002033013661715457022363 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2015-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package body GNATCOLL.Projects.Krunch is ------------ -- Krunch -- ------------ procedure Krunch (Buffer : in out String; Len : in out Natural; Maxlen : Natural; No_Predef : Boolean) is pragma Assert (Buffer'First = 1); -- This is a documented requirement; the assert turns off index warnings B1 : Character renames Buffer (1); Curlen : Natural; Krlen : Natural; Num_Seps : Natural; Startloc : Natural; J : Natural; begin -- Deal with special predefined children cases. Startloc is the first -- location for the krunch, set to 1, except for the predefined children -- case, where it is set to 3, to start after the standard prefix. if No_Predef then Startloc := 1; Curlen := Len; Krlen := Maxlen; elsif Len >= 18 and then Buffer (1 .. 17) = "ada-wide_text_io-" then Startloc := 3; Buffer (2 .. 5) := "-wt-"; Buffer (6 .. Len - 12) := Buffer (18 .. Len); Curlen := Len - 12; Krlen := 8; elsif Len >= 23 and then Buffer (1 .. 22) = "ada-wide_wide_text_io-" then Startloc := 3; Buffer (2 .. 5) := "-zt-"; Buffer (6 .. Len - 17) := Buffer (23 .. Len); Curlen := Len - 17; Krlen := 8; elsif Len >= 4 and then Buffer (1 .. 4) = "ada-" then Startloc := 3; Buffer (2 .. Len - 2) := Buffer (4 .. Len); Curlen := Len - 2; Krlen := 8; elsif Len >= 5 and then Buffer (1 .. 5) = "gnat-" then Startloc := 3; Buffer (2 .. Len - 3) := Buffer (5 .. Len); Curlen := Len - 3; Krlen := 8; elsif Len >= 7 and then Buffer (1 .. 7) = "system-" then Startloc := 3; Buffer (2 .. Len - 5) := Buffer (7 .. Len); Curlen := Len - 5; Krlen := 8; elsif Len >= 11 and then Buffer (1 .. 11) = "interfaces-" then Startloc := 3; Buffer (2 .. Len - 9) := Buffer (11 .. Len); Curlen := Len - 9; Krlen := 8; -- For the renamings in the obsolescent section, we also force -- krunching to 8 characters, but no other special processing is -- required here. Note that text_io and calendar are already short -- enough anyway. elsif (Len = 9 and then Buffer (1 .. 9) = "direct_io") or else (Len = 10 and then Buffer (1 .. 10) = "interfaces") or else (Len = 13 and then Buffer (1 .. 13) = "io_exceptions") or else (Len = 12 and then Buffer (1 .. 12) = "machine_code") or else (Len = 13 and then Buffer (1 .. 13) = "sequential_io") or else (Len = 20 and then Buffer (1 .. 20) = "unchecked_conversion") or else (Len = 22 and then Buffer (1 .. 22) = "unchecked_deallocation") then Startloc := 1; Krlen := 8; Curlen := Len; -- Special case of a child unit whose parent unit is a single letter -- that is A, G, I, or S. In order to prevent confusion with krunched -- names of predefined units use a tilde rather than a minus as the -- second character of the file name. elsif Len > 1 and then Buffer (2) = '-' and then (B1 = 'a' or else B1 = 'g' or else B1 = 'i' or else B1 = 's') and then Len <= Maxlen then Buffer (2) := '~'; return; -- Normal case, not a predefined file else Startloc := 1; Curlen := Len; Krlen := Maxlen; end if; -- Immediate return if file name is short enough now if Curlen <= Krlen then Len := Curlen; return; end if; -- If string contains Wide_Wide, replace by a single z J := Startloc; while J <= Curlen - 8 loop if Buffer (J .. J + 8) = "wide_wide" and then (J = Startloc or else Buffer (J - 1) = '-' or else Buffer (J - 1) = '_') and then (J + 8 = Curlen or else Buffer (J + 9) = '-' or else Buffer (J + 9) = '_') then Buffer (J) := 'z'; Buffer (J + 1 .. Curlen - 8) := Buffer (J + 9 .. Curlen); Curlen := Curlen - 8; end if; J := J + 1; end loop; -- For now, refuse to krunch a name that contains an ESC character (wide -- character sequence) since it's too much trouble to do this right ??? for J in 1 .. Curlen loop if Buffer (J) = ASCII.ESC then return; end if; end loop; -- Count number of separators (minus signs and underscores) and for now -- replace them by spaces. We keep them around till the end to control -- the krunching process, and then we eliminate them as the last step Num_Seps := 0; for J in Startloc .. Curlen loop if Buffer (J) = '-' or else Buffer (J) = '_' then Buffer (J) := ' '; Num_Seps := Num_Seps + 1; end if; end loop; -- Now we do the one character at a time krunch till we are short enough while Curlen - Num_Seps > Krlen loop declare Long_Length : Natural := 0; Long_Last : Natural := 0; Piece_Start : Natural; Ptr : Natural; begin Ptr := Startloc; -- Loop through pieces to find longest piece while Ptr <= Curlen loop Piece_Start := Ptr; -- Loop through characters in one piece of name while Ptr <= Curlen and then Buffer (Ptr) /= ' ' loop Ptr := Ptr + 1; end loop; if Ptr - Piece_Start > Long_Length then Long_Length := Ptr - Piece_Start; Long_Last := Ptr - 1; end if; Ptr := Ptr + 1; end loop; -- Remove last character of longest piece if Long_Last < Curlen then Buffer (Long_Last .. Curlen - 1) := Buffer (Long_Last + 1 .. Curlen); end if; Curlen := Curlen - 1; end; end loop; -- Final step, remove the spaces Len := 0; for J in 1 .. Curlen loop if Buffer (J) /= ' ' then Len := Len + 1; Buffer (Len) := Buffer (J); end if; end loop; return; end Krunch; end GNATCOLL.Projects.Krunch; gnatcoll-core-21.0.0/src/gnatcoll-io-remote-unix.adb0000644000175000017500000005242113661715457022131 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNAT.Expect; use GNAT.Expect; with GNAT.Regpat; use GNAT.Regpat; with GNATCOLL.Utils; package body GNATCOLL.IO.Remote.Unix is procedure Free (Args : in out GNAT.OS_Lib.Argument_List); -- Free all strings in Args. ---------- -- Free -- ---------- procedure Free (Args : in out GNAT.OS_Lib.Argument_List) is begin for J in Args'Range loop Free (Args (J)); end loop; end Free; ----------------- -- Current_Dir -- ----------------- function Current_Dir (Exec : access Server_Record'Class) return FS_String is Args : GNAT.OS_Lib.Argument_List := (1 => new String'("pwd")); Output : String_Access; Status : Boolean; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Status then declare -- Don't try to translate the string into a directory, as this -- is all handled later at VFS level. Result : constant FS_String := FS_String (Output.all); begin Free (Output); return Result; end; else return "/"; end if; end Current_Dir; -------------- -- Home_Dir -- -------------- function Home_Dir (Exec : access Server_Record'Class) return FS_String is Args : GNAT.OS_Lib.Argument_List := (new String'("echo"), new String'("$HOME")); Output : String_Access; Status : Boolean; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Status then declare -- Don't try to translate the string into a directory, as this -- is all handled later at VFS level. Result : constant FS_String := FS_String (Output.all); begin Free (Output); return Result; end; else return "/"; end if; end Home_Dir; ------------- -- Tmp_Dir -- ------------- function Tmp_Dir (Exec : access Server_Record'Class) return FS_String is Args : GNAT.OS_Lib.Argument_List := (new String'("echo"), new String'("$TMP")); Output : String_Access; Status : Boolean; begin Exec.Execute_Remotely (Args, Output, Status); if not Status then GNAT.OS_Lib.Free (Args (2)); Args (2) := new String'("$TMPDIR"); Exec.Execute_Remotely (Args, Output, Status); end if; Free (Args); if Status then declare -- Don't try to translate the string into a directory, as this -- is all handled later at VFS level. Result : constant FS_String := FS_String (Output.all); begin Free (Output); return Result; end; else return "/tmp/"; end if; end Tmp_Dir; ------------------------ -- Get_Logical_Drives -- ------------------------ function Get_Logical_Drives (Exec : access Server_Record'Class) return String_List_Access is -- No drives on unix pragma Unreferenced (Exec); begin return null; end Get_Logical_Drives; -------------------- -- Locate_On_Path -- -------------------- function Locate_On_Path (Exec : access Server_Record'Class; Base : FS_String) return FS_String is Args : GNAT.OS_Lib.Argument_List := (new String'("which"), new String'(String (Base))); Output : String_Access; Status : Boolean; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Status then declare -- Don't try to translate the string into a directory, as this -- is all handled later at VFS level. Result : constant FS_String := FS_String (Output.all); begin Free (Output); return Result; end; else return ""; end if; end Locate_On_Path; --------------------- -- Is_Regular_File -- --------------------- function Is_Regular_File (Exec : access Server_Record'Class; File : FS_String) return Boolean is Args : GNAT.OS_Lib.Argument_List := (new String'("test"), new String'("-r"), new String'("""" & String (File) & """")); Status : Boolean; begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Is_Regular_File; ---------- -- Size -- ---------- function Size (Exec : access Server_Record'Class; File : FS_String) return Long_Integer is Args : GNAT.OS_Lib.Argument_List := (new String'("stat"), new String'("-s"), new String'("""" & String (File) & """")); Status : Boolean; Regexp : constant Pattern_Matcher := Compile ("st_size=(\d+)"); Output : String_Access; Matched : Match_Array (0 .. 1); Size : Long_Integer := 0; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Status and then Output /= null then Match (Regexp, Output.all, Matched); if Matched (1) /= No_Match then Size := Long_Integer'Value (Output (Matched (1).First .. Matched (1).Last)); end if; end if; Free (Output); return Size; end Size; ------------------ -- Is_Directory -- ------------------ function Is_Directory (Exec : access Server_Record'Class; File : FS_String) return Boolean is Args : GNAT.OS_Lib.Argument_List := (new String'("test"), new String'("-d"), new String'("""" & String (File) & """")); Status : Boolean; begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Is_Directory; ---------------------- -- Is_Symbolic_Link -- ---------------------- function Is_Symbolic_Link (Exec : access Server_Record'Class; File : FS_String) return Boolean is Args : GNAT.OS_Lib.Argument_List := (new String'("test"), new String'("-L"), new String'("""" & String (File) & """")); Status : Boolean; begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Is_Symbolic_Link; --------------------- -- File_Time_Stamp -- --------------------- function File_Time_Stamp (Exec : access Server_Record'Class; File : FS_String) return Ada.Calendar.Time is Args : GNAT.OS_Lib.Argument_List := (new String'("ls"), new String'("-l"), new String'("--time-style=full-iso"), new String'("""" & String (File) & """"), new String'("2>"), new String'("/dev/null")); Status : Boolean; Regexp : constant Pattern_Matcher := Compile ("(\d\d\d\d[-]\d\d[-]\d\d)\s+(\d\d:\d\d:\d\d[.]\d+)\s+"); Matched : Match_Array (0 .. 2); Output : String_Access; Year : Natural; Month : Natural; Day : Natural; Hour : Natural; Minute : Natural; Second : Ada.Calendar.Day_Duration; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Status and then Output /= null then Match (Regexp, Output.all, Matched); if Matched (0) = No_Match then Free (Output); return GNATCOLL.Utils.No_Time; end if; Year := Natural'Value (Output (Matched (1).First .. Matched (1).First + 3)); Month := Natural'Value (Output (Matched (1).First + 5 .. Matched (1).First + 6)); Day := Natural'Value (Output (Matched (1).First + 8 .. Matched (1).First + 9)); Hour := Natural'Value (Output (Matched (2).First .. Matched (2).First + 1)); Minute := Natural'Value (Output (Matched (2).First + 3 .. Matched (2).First + 4)); Second := Ada.Calendar.Day_Duration'Value (Output (Matched (2).First + 6 .. Matched (2).Last)); Second := Second + (60.0 * Ada.Calendar.Day_Duration (Minute)) + (3600.0 * Ada.Calendar.Day_Duration (Hour)); Free (Output); return Ada.Calendar.Time_Of (Year, Month, Day, Second); end if; Free (Output); return GNATCOLL.Utils.No_Time; end File_Time_Stamp; ----------------- -- Is_Readable -- ----------------- function Is_Readable (Exec : access Server_Record'Class; File : FS_String) return Boolean is Args : GNAT.OS_Lib.Argument_List := (new String'("test"), new String'("-r"), new String'("""" & String (File) & """")); Status : Boolean; begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Is_Readable; ----------------- -- Is_Writable -- ----------------- function Is_Writable (Exec : access Server_Record'Class; File : FS_String) return Boolean is Args : GNAT.OS_Lib.Argument_List := (new String'("test"), new String'("-w"), new String'("""" & String (File) & """")); Status : Boolean; begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Is_Writable; ------------------ -- Set_Writable -- ------------------ procedure Set_Writable (Exec : access Server_Record'Class; File : FS_String; State : Boolean) is Args : GNAT.OS_Lib.Argument_List := (1 => new String'("chmod"), 2 => new String'("u+w"), 3 => new String'("""" & String (File) & """")); Status : Boolean; pragma Unreferenced (Status); begin if not State then Args (2).all := "u-w"; end if; Exec.Execute_Remotely (Args, Status); Free (Args); end Set_Writable; ------------------ -- Set_Readable -- ------------------ procedure Set_Readable (Exec : access Server_Record'Class; File : FS_String; State : Boolean) is Args : GNAT.OS_Lib.Argument_List := (1 => new String'("chmod"), 2 => new String'("u+r"), 3 => new String'("""" & String (File) & """")); Status : Boolean; pragma Unreferenced (Status); begin if not State then Args (2).all := "u-r"; end if; Exec.Execute_Remotely (Args, Status); Free (Args); end Set_Readable; ------------ -- Rename -- ------------ procedure Rename (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean) is Args : GNAT.OS_Lib.Argument_List := (new String'("mv"), new String'("""" & String (From) & """"), new String'("""" & String (Dest) & """")); begin Exec.Execute_Remotely (Args, Success); Free (Args); end Rename; ---------- -- Copy -- ---------- procedure Copy (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean) is Args : GNAT.OS_Lib.Argument_List := (new String'("cp"), new String'("-f"), new String'("""" & String (From) & """"), new String'("""" & String (Dest) & """")); begin Exec.Execute_Remotely (Args, Success); Free (Args); end Copy; ------------ -- Delete -- ------------ procedure Delete (Exec : access Server_Record'Class; File : FS_String; Success : out Boolean) is Args : GNAT.OS_Lib.Argument_List := (new String'("rm"), new String'("-f"), new String'("""" & String (File) & """")); begin Exec.Execute_Remotely (Args, Success); Free (Args); end Delete; --------------------- -- Read_Whole_File -- --------------------- function Read_Whole_File (Exec : access Server_Record'Class; File : FS_String) return GNAT.Strings.String_Access is Args : GNAT.OS_Lib.Argument_List := (new String'("cat"), new String'("""" & String (File) & """")); Status : Boolean; Output : String_Access; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); return Output; end Read_Whole_File; --------------------- -- Read_Whole_File -- --------------------- function Read_Whole_File (Exec : access Server_Record'Class; File : FS_String) return GNATCOLL.Strings.XString is Args : GNAT.OS_Lib.Argument_List := (new String'("cat"), new String'("""" & String (File) & """")); Status : Boolean; Output : String_Access; Result : XString; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); Result.Set (Output.all); -- needs a copy of the string Free (Output); return Result; end Read_Whole_File; ---------------- -- Write_File -- ---------------- function Write_File (Exec : access Server_Record'Class; File : FS_String; Content : String) return Boolean is Pd : Process_Descriptor_Access; Args : GNAT.OS_Lib.Argument_List := (1 => new String'("cat"), 2 => new String'(">"), 3 => new String'("""" & String (File) & """"), 4 => new String'("<<"), 5 => new String'("GPSEOF")); Regexp : constant Pattern_Matcher := Compile ("[>] ", Single_Line or Multiple_Lines); Res : Expect_Match; begin Exec.Spawn_Remotely (Descriptor => Pd, Args => Args); Send (Pd.all, Content); Send (Pd.all, "GPSEOF"); loop Expect (Pd.all, Res, Regexp, 5000); if Res = Expect_Timeout then Flush (Pd.all); Close (Pd.all); exit; end if; end loop; Free (Args); return True; exception when Process_Died => Close (Pd.all); Free (Args); return False; end Write_File; ---------------- -- Change_Dir -- ---------------- function Change_Dir (Exec : access Server_Record'Class; Dir : FS_String) return Boolean is Args : GNAT.OS_Lib.Argument_List := (new String'("cd"), new String'("""" & String (Dir) & """")); Status : Boolean; begin Exec.Execute_Remotely (Args, Status); Free (Args); return Status; end Change_Dir; -------------- -- Read_Dir -- -------------- function Read_Dir (Exec : access Server_Record'Class; Dir : FS_String; Dirs_Only : Boolean := False; Files_Only : Boolean := False) return GNAT.Strings.String_List is function Create_Args return GNAT.OS_Lib.Argument_List; -- Return dir arguments following the Dirs_Only and Files_Only arguments ----------------- -- Create_Args -- ----------------- function Create_Args return GNAT.OS_Lib.Argument_List is begin -- Launch with sh to be able to redirect stderr to /dev/null, even -- when using (t)csh if Dirs_Only then return (new String'("sh"), new String'("-c"), new String'("ls -AL1F '" & String (Dir) & "' 2> /dev/null | grep /$")); elsif Files_Only then return (new String'("sh"), new String'("-c"), new String'("ls -AL1F '" & String (Dir) & "' 2> /dev/null | grep -v /$ | " & "sed -e 's/[*=@\|]$//'")); else return (new String'("sh"), new String'("-c"), new String'("ls"), new String'("-A1"), new String'("'" & String (Dir) & "'")); end if; end Create_Args; Args : GNAT.OS_Lib.Argument_List := Create_Args; Status : Boolean; Output : String_Access; Regexp : constant Pattern_Matcher := Compile ("^(.+)$", Multiple_Lines); Matched : Match_Array (0 .. 1); Index : Integer; Nb_Files : Natural; begin Exec.Execute_Remotely (Args, Output, Status); Free (Args); if Output /= null then Index := Output'First; Nb_Files := 0; while Index <= Output'Last loop Match (Regexp, Output (Index .. Output'Last), Matched); exit when Matched (0) = No_Match; Index := Matched (1).Last + 1; if Output (Matched (1).First .. Matched (1).Last) /= "." and then Output (Matched (1).First .. Matched (1).Last) /= ".." then Nb_Files := Nb_Files + 1; end if; end loop; declare List : String_List (1 .. Nb_Files); File_Idx : Natural; begin Index := Output'First; File_Idx := List'First; while Index /= Output'Last loop Match (Regexp, Output.all, Matched, Index); exit when Matched (0) = No_Match; Index := Matched (1).Last + 1; if Output (Matched (1).First .. Matched (1).Last) /= "." and then Output (Matched (1).First .. Matched (1).Last) /= ".." then List (File_Idx) := new String' (Output (Matched (1).First .. Matched (1).Last)); File_Idx := File_Idx + 1; end if; end loop; return List; end; end if; Free (Output); return (1 .. 0 => null); end Read_Dir; -------------- -- Make_Dir -- -------------- function Make_Dir (Exec : access Server_Record'Class; Dir : FS_String; Recursive : Boolean) return Boolean is Status : Boolean; Args : GNAT.OS_Lib.Argument_List := (1 => new String'("mkdir"), 2 => new String'("-p"), 3 => new String'("'" & String (Dir) & "'")); begin if Recursive then Exec.Execute_Remotely (Args, Status); else Exec.Execute_Remotely ((1 => Args (1), 2 => Args (3)), Status); end if; Free (Args); return Status; end Make_Dir; -------------- -- Copy_Dir -- -------------- procedure Copy_Dir (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean) is Args : GNAT.OS_Lib.Argument_List := (new String'("cp"), new String'("-rf"), new String'("'" & String (From) & "'"), new String'("'" & String (Dest) & "'")); begin Exec.Execute_Remotely (Args, Success); Free (Args); end Copy_Dir; ---------------- -- Delete_Dir -- ---------------- procedure Delete_Dir (Exec : access Server_Record'Class; Dir : FS_String; Recursive : Boolean; Success : out Boolean) is Args : GNAT.OS_Lib.Argument_List := (1 => new String'("rm"), 2 => new String'("-r"), 3 => new String'("'" & String (Dir) & "'")); begin if Recursive then Free (Args (2)); Args (2) := new String'("-rf"); end if; Exec.Execute_Remotely (Args, Success); Free (Args); end Delete_Dir; end GNATCOLL.IO.Remote.Unix; gnatcoll-core-21.0.0/src/gnatcoll-io-native.adb0000644000175000017500000005730413661715457021150 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Directories; with Ada.Exceptions; with Ada.Unchecked_Deallocation; with System; with Ada.Calendar.Formatting; use Ada.Calendar; with Ada.Calendar.Time_Zones; use Ada.Calendar.Time_Zones; with GNAT.Directory_Operations; use GNAT.Directory_Operations; with GNAT.OS_Lib; with GNAT.Strings; use GNAT.Strings; with Interfaces.C; use Interfaces.C; with Interfaces.C.Strings; use Interfaces.C.Strings; with GNATCOLL.Mmap; with GNATCOLL.Path; use GNATCOLL.Path; with GNATCOLL.Utils; use GNATCOLL.Utils; package body GNATCOLL.IO.Native is package body Codec is separate; -- Separate implementation for Windows and Unix ------------ -- Create -- ------------ function Create (Path : FS_String) return File_Access is begin return new Native_File_Record' (Ref_Count => 1, Full => new FS_String'(From_Unix (Local_FS, Path)), Normalized => null, Normalized_And_Resolved => null, Kind => Unknown); end Create; ------------------------ -- Dispatching_Create -- ------------------------ overriding function Dispatching_Create (Ref : not null access Native_File_Record; Full_Path : FS_String) return File_Access is pragma Unreferenced (Ref); begin return Create (Full_Path); end Dispatching_Create; ------------- -- To_UTF8 -- ------------- overriding function To_UTF8 (Ref : not null access Native_File_Record; Path : FS_String) return String is pragma Unreferenced (Ref); begin return Codec.To_UTF8 (Path); end To_UTF8; --------------- -- From_UTF8 -- --------------- overriding function From_UTF8 (Ref : not null access Native_File_Record; Path : String) return FS_String is pragma Unreferenced (Ref); begin return Codec.From_UTF8 (Path); end From_UTF8; ----------------- -- Current_Dir -- ----------------- function Current_Dir return File_Access is D : constant GNAT.Directory_Operations.Dir_Name_Str := GNAT.Directory_Operations.Get_Current_Dir; Ret : File_Access; begin Ret := Create (FS_String (D)); return Ret; end Current_Dir; -------------- -- Home_Dir -- -------------- function Home_Dir return File_Access is HOME : GNAT.Strings.String_Access := GNAT.OS_Lib.Getenv ("HOME"); Tmp : GNAT.Strings.String_Access; begin if HOME.all = "" then Free (HOME); HOME := GNAT.OS_Lib.Getenv ("USERPROFILE"); if HOME.all = "" then Free (HOME); return Create (Get_Root (Local_FS, "")); end if; end if; if HOME'Length > 2 and then HOME (HOME'First) = '%' and then HOME (HOME'Last) = '%' then -- Some Windows systems set %HOME% to another env variable, e.g. -- %USERPROFILE% Tmp := HOME; HOME := GNAT.OS_Lib.Getenv (Tmp (Tmp'First + 1 .. Tmp'Last - 1)); Free (Tmp); end if; declare -- ??? Convert from display charset to filesystem charset ? Result : constant FS_String := From_Unix (Local_FS, FS_String (HOME.all)); begin Free (HOME); return Create (Result); end; end Home_Dir; ----------------------- -- Get_Tmp_Directory -- ----------------------- function Get_Tmp_Directory return File_Access is function Internal return chars_ptr; pragma Import (C, Internal, "__gnatcoll_get_tmp_dir"); procedure c_free (C : chars_ptr); pragma Import (C, c_free, "free"); C_Str : constant chars_ptr := Internal; Str : constant FS_String := FS_String (GNAT.Directory_Operations.Format_Pathname (To_Ada (Value (C_Str)))); begin -- Since the allocation was done in C (strdup), we use directly the -- C version of free. This is probably more reliable, and more -- importantly, this works correctly with our own version of -- s-memory.adb (when GPS_MEMORY_MONITOR=1) c_free (C_Str); return Create (Ensure_Directory (Local_FS, Str)); end Get_Tmp_Directory; ------------------------ -- Get_Logical_Drives -- ------------------------ function Get_Logical_Drives return File_Array is function Internal (Buffer : System.Address; Length : Integer) return Integer; pragma Import (C, Internal, "__gnatcoll_get_logical_drive_strings"); Len : Integer; Last : Natural; N : Natural; begin -- First get the size of the buffer needed to contain the drives Len := Internal (System.Null_Address, 0); if Len = 0 then return (1 .. 0 => <>); end if; declare -- Use the returned length for creating the buffer. Do not forget -- to add room for the trailing \n Buffer : aliased FS_String (1 .. Len + 1); begin Len := Internal (Buffer'Address, Len); N := 0; for J in 1 .. Len loop if Buffer (J) = ASCII.NUL then N := N + 1; end if; end loop; declare Ret : File_Array (1 .. N); begin N := 1; Last := Buffer'First; for J in 1 .. Len loop if Buffer (J) = ASCII.NUL then Ret (N) := Create (GNATCOLL.Path.Path (Local_FS, Buffer (Last .. Last), "", "")); N := N + 1; Last := J + 1; end if; end loop; return Ret; end; end; end Get_Logical_Drives; -------------- -- Is_Local -- -------------- overriding function Is_Local (File : Native_File_Record) return Boolean is pragma Unreferenced (File); begin return True; end Is_Local; ------------ -- Get_FS -- ------------ overriding function Get_FS (File : not null access Native_File_Record) return FS_Type is pragma Unreferenced (File); begin return Local_FS; end Get_FS; ---------------------- -- Resolve_Symlinks -- ---------------------- overriding procedure Resolve_Symlinks (File : not null access Native_File_Record) is Is_Dir_Path : Boolean; begin if File.Normalized_And_Resolved = null then Is_Dir_Path := Path.Is_Dir_Name (File.Get_FS, File.Full.all); declare -- We have to pass "" for the directory, in case File.Full is a -- relative path name. That might be surprising to the application -- since the current directory might have changed since File was -- created. Norm : constant String := GNAT.OS_Lib.Normalize_Pathname (String (File.Full.all), Directory => "", Resolve_Links => True); begin -- Normalize_Pathname sometimes removes the trailing dir separator -- We need to take care of it then. if not Is_Dir_Path or else Norm (Norm'Last) = GNAT.OS_Lib.Directory_Separator then if File.Normalized /= null and then FS_String (Norm) = File.Normalized.all then File.Normalized_And_Resolved := File.Normalized; else File.Normalized_And_Resolved := new FS_String'(FS_String (Norm)); end if; else if File.Normalized /= null and then FS_String (Norm) & GNAT.OS_Lib.Directory_Separator = File.Normalized.all then File.Normalized_And_Resolved := File.Normalized; else File.Normalized_And_Resolved := new FS_String' (FS_String (Norm) & GNAT.OS_Lib.Directory_Separator); end if; end if; end; end if; end Resolve_Symlinks; --------------------- -- Is_Regular_File -- --------------------- overriding function Is_Regular_File (File : not null access Native_File_Record) return Boolean is begin return GNAT.OS_Lib.Is_Regular_File (String (File.Full.all)); end Is_Regular_File; ---------- -- Size -- ---------- overriding function Size (File : not null access Native_File_Record) return Long_Integer is Fd : constant GNAT.OS_Lib.File_Descriptor := GNAT.OS_Lib.Open_Read (String (File.Full.all), Fmode => GNAT.OS_Lib.Binary); pragma Warnings (Off); Result : constant Long_Integer := Long_Integer (GNAT.OS_Lib.File_Length (Fd)); pragma Warnings (On); begin GNAT.OS_Lib.Close (Fd); return Result; end Size; ------------------ -- Is_Directory -- ------------------ overriding function Is_Directory (File : not null access Native_File_Record) return Boolean is begin if GNAT.OS_Lib.Directory_Separator = '\' and then File.Full (File.Full'First .. File.Full'First + 1) = "\\" then -- There is an issue with (at least) GNAT 6.2 when Is_Directory -- returns False for Windows network paths (e.g. \\host\shared\). -- In this case, we try to open the directory and see if it works. declare Dir : GNAT.Directory_Operations.Dir_Type; begin GNAT.Directory_Operations.Open (Dir, String (File.Full.all)); GNAT.Directory_Operations.Close (Dir); return True; exception when GNAT.Directory_Operations.Directory_Error => return False; end; else return GNAT.OS_Lib.Is_Directory (String (File.Full.all)); end if; end Is_Directory; ---------------------- -- Is_Symbolic_Link -- ---------------------- overriding function Is_Symbolic_Link (File : not null access Native_File_Record) return Boolean is begin return GNAT.OS_Lib.Is_Symbolic_Link (String (File.Full.all)); end Is_Symbolic_Link; --------------------- -- File_Time_Stamp -- --------------------- TZ : constant Time_Offset := UTC_Time_Offset; -- Time zone cache, assuming that the OS will not change time zones while -- this partition is running. overriding function File_Time_Stamp (File : not null access Native_File_Record) return Ada.Calendar.Time is T : constant GNAT.OS_Lib.OS_Time := GNAT.OS_Lib.File_Time_Stamp (String (File.Full.all)); Year : GNAT.OS_Lib.Year_Type; Month : GNAT.OS_Lib.Month_Type; Day : GNAT.OS_Lib.Day_Type; Hour : GNAT.OS_Lib.Hour_Type; Minute : GNAT.OS_Lib.Minute_Type; Second : GNAT.OS_Lib.Second_Type; use type GNAT.OS_Lib.OS_Time; begin if T = GNAT.OS_Lib.Invalid_Time then return GNATCOLL.Utils.No_Time; end if; GNAT.OS_Lib.GM_Split (T, Year, Month, Day, Hour, Minute, Second); return Ada.Calendar.Formatting.Time_Of (Year => Year_Number (Year), Month => Month_Number (Month), Day => Day_Number (Day), Hour => Formatting.Hour_Number (Hour), Minute => Formatting.Minute_Number (Minute), Second => Formatting.Second_Number (Second), Sub_Second => 0.0, Time_Zone => TZ); end File_Time_Stamp; ----------------- -- Is_Readable -- ----------------- overriding function Is_Readable (File : not null access Native_File_Record) return Boolean is begin return GNAT.OS_Lib.Is_Readable_File (String (File.Full.all)); end Is_Readable; ----------------- -- Is_Writable -- ----------------- overriding function Is_Writable (File : not null access Native_File_Record) return Boolean is begin return GNAT.OS_Lib.Is_Writable_File (String (File.Full.all)); end Is_Writable; ------------------ -- Set_Writable -- ------------------ overriding procedure Set_Writable (File : not null access Native_File_Record; State : Boolean) is begin if State then GNAT.OS_Lib.Set_Writable (String (File.Full.all)); else GNAT.OS_Lib.Set_Non_Writable (String (File.Full.all)); end if; end Set_Writable; ------------------ -- Set_Readable -- ------------------ overriding procedure Set_Readable (File : not null access Native_File_Record; State : Boolean) is begin if State then GNAT.OS_Lib.Set_Readable (String (File.Full.all)); else GNAT.OS_Lib.Set_Non_Readable (String (File.Full.all)); end if; end Set_Readable; ------------ -- Rename -- ------------ overriding procedure Rename (From : not null access Native_File_Record; Dest : not null access Native_File_Record; Success : out Boolean) is begin GNAT.OS_Lib.Rename_File (String (From.Full.all), String (Dest.Full.all), Success); exception when others => Success := False; end Rename; ---------- -- Copy -- ---------- overriding procedure Copy (From : not null access Native_File_Record; Dest : FS_String; Success : out Boolean) is begin GNAT.OS_Lib.Copy_File (String (From.Full.all), String (Dest), Success, Mode => GNAT.OS_Lib.Overwrite, Preserve => GNAT.OS_Lib.Full); exception when others => Success := False; end Copy; ------------ -- Delete -- ------------ overriding procedure Delete (File : not null access Native_File_Record; Success : out Boolean) is begin GNAT.OS_Lib.Delete_File (String (File.Full.all), Success); end Delete; --------------------- -- Read_Whole_File -- --------------------- overriding function Read_Whole_File (File : not null access Native_File_Record) return GNAT.Strings.String_Access is begin return GNATCOLL.Mmap.Read_Whole_File (String (File.Full.all)); exception when others => return null; end Read_Whole_File; --------------------- -- Read_Whole_File -- --------------------- overriding function Read_Whole_File (File : not null access Native_File_Record) return GNATCOLL.Strings.XString is begin return GNATCOLL.Mmap.Read_Whole_File (String (File.Full.all)); exception when others => return GNATCOLL.Strings.Null_XString; end Read_Whole_File; ---------------- -- Open_Write -- ---------------- overriding procedure Open_Write (File : not null access Native_File_Record; Append : Boolean := False; FD : out GNAT.OS_Lib.File_Descriptor; Error : out Ada.Strings.Unbounded.Unbounded_String) is use type GNAT.OS_Lib.File_Descriptor; begin if Append then FD := GNAT.OS_Lib.Open_Read_Write (String (File.Full.all), Fmode => GNAT.OS_Lib.Binary); else FD := GNAT.OS_Lib.Create_File (String (File.Full.all), Fmode => GNAT.OS_Lib.Binary); end if; if FD = GNAT.OS_Lib.Invalid_FD then Error := Ada.Strings.Unbounded.To_Unbounded_String (GNAT.OS_Lib.Errno_Message); else Error := Ada.Strings.Unbounded.Null_Unbounded_String; end if; if Append then GNAT.OS_Lib.Lseek (FD, 0, GNAT.OS_Lib.Seek_End); -- It is impossible to obtain return value of lseek to check for -- errors. end if; exception when E : others => FD := GNAT.OS_Lib.Invalid_FD; Error := Ada.Strings.Unbounded.To_Unbounded_String (Ada.Exceptions.Exception_Information (E)); end Open_Write; ----------- -- Close -- ----------- overriding procedure Close (File : not null access Native_File_Record; FD : GNAT.OS_Lib.File_Descriptor; Success : out Boolean) is pragma Unreferenced (File); use type GNAT.OS_Lib.File_Descriptor; begin if FD /= GNAT.OS_Lib.Invalid_FD then GNAT.OS_Lib.Close (FD); Success := True; else Success := False; end if; end Close; ---------------- -- Change_Dir -- ---------------- overriding function Change_Dir (Dir : not null access Native_File_Record) return Boolean is begin GNAT.Directory_Operations.Change_Dir (String (Dir.Full.all)); return True; exception when others => return False; end Change_Dir; -------------- -- Read_Dir -- -------------- overriding function Read_Dir (Dir : not null access Native_File_Record; Dirs_Only : Boolean := False; Files_Only : Boolean := False) return GNAT.Strings.String_List is Name : constant String := String (Ensure_Directory (Local_FS, Dir.Full.all)); D : GNAT.Directory_Operations.Dir_Type; Item : String (1 .. 1024); Last : Natural; Ret : GNAT.Strings.String_List_Access; Tmp : GNAT.Strings.String_List_Access; N : Natural := 0; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (GNAT.Strings.String_List, GNAT.Strings.String_List_Access); begin GNAT.Directory_Operations.Open (D, String (Dir.Full.all)); loop GNAT.Directory_Operations.Read (D, Item, Last); exit when Last = 0; if Item (1 .. Last) /= "." and then Item (1 .. Last) /= ".." and then (not Dirs_Only or else GNAT.OS_Lib.Is_Directory (Name & Item (1 .. Last))) and then (not Files_Only or else GNAT.OS_Lib.Is_Regular_File (Name & Item (1 .. Last))) then if Ret = null then Ret := new GNAT.Strings.String_List (1 .. 10); elsif N = Ret'Last then Tmp := new GNAT.Strings.String_List (1 .. Ret'Length * 2); Tmp (Ret'Range) := Ret.all; Unchecked_Free (Ret); Ret := Tmp; end if; N := N + 1; Ret (N) := new String'(Item (1 .. Last)); end if; end loop; GNAT.Directory_Operations.Close (D); if N = 0 then return (1 .. 0 => <>); end if; declare List : constant GNAT.Strings.String_List := Ret (1 .. N); begin Unchecked_Free (Ret); return List; end; end Read_Dir; -------------- -- Make_Dir -- -------------- overriding function Make_Dir (Dir : not null access Native_File_Record; Recursive : Boolean) return Boolean is begin if Recursive then Ada.Directories.Create_Path (String (Dir.Full.all)); else Ada.Directories.Create_Directory (String (Dir.Full.all)); end if; return True; exception when GNAT.Directory_Operations.Directory_Error => return False; end Make_Dir; ---------------- -- Remove_Dir -- ---------------- overriding procedure Remove_Dir (Dir : not null access Native_File_Record; Recursive : Boolean; Success : out Boolean) is begin GNAT.Directory_Operations.Remove_Dir (String (Dir.Full.all), Recursive); Success := True; exception when GNAT.Directory_Operations.Directory_Error => Success := False; end Remove_Dir; -------------- -- Copy_Dir -- -------------- overriding procedure Copy_Dir (From : not null access Native_File_Record; Dest : FS_String; Success : out Boolean) is begin if not Is_Directory (From) then Success := False; return; end if; if not GNAT.OS_Lib.Is_Directory (String (Dest)) then begin GNAT.Directory_Operations.Make_Dir (String (Dest)); exception when others => Success := False; return; end; end if; declare Files : GNAT.Strings.String_List := Read_Dir (From); begin Success := True; for F in Files'Range loop declare Tmp_From : File_Access := Create (GNATCOLL.Path.Path (Local_FS, "", From.Full.all, FS_String (Files (F).all))); To_Str : constant FS_String := GNATCOLL.Path.Path (Local_FS, "", Dest, FS_String (Files (F).all)); begin if Is_Directory (Tmp_From) then Copy_Dir (Tmp_From, To_Str, Success); Unref (Tmp_From); exit when not Success; else Copy (Tmp_From, To_Str, Success); Unref (Tmp_From); exit when not Success; end if; end; GNAT.Strings.Free (Files (F)); end loop; exception when others => for J in Files'Range loop GNAT.Strings.Free (Files (J)); end loop; Success := False; end; exception when others => Success := False; end Copy_Dir; --------------------------- -- Copy_File_Permissions -- --------------------------- overriding procedure Copy_File_Permissions (From, To : not null access Native_File_Record; Success : out Boolean) is begin GNAT.OS_Lib.Copy_File_Attributes (From => String (From.Full.all), To => String (To.Full.all), Success => Success, Copy_Timestamp => False, Copy_Permissions => True); end Copy_File_Permissions; end GNATCOLL.IO.Native; gnatcoll-core-21.0.0/src/gnatcoll-json.adb0000644000175000017500000015141313661715457020222 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2011-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Containers; use Ada.Containers; with Ada.Exceptions; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Text_IO; with Ada.Unchecked_Deallocation; with GNATCOLL.Atomic; use GNATCOLL.Atomic; with GNATCOLL.JSON.Utility; with GNATCOLL.Strings; use GNATCOLL.Strings; package body GNATCOLL.JSON is type Text_Position is record Index : Natural := 0; -- Array index in the input string. For valid positions, this is -- positive. Line : Natural := 0; -- Logical line number. For valid positions, this is positive. Column : Natural := 0; -- Logical column number. For valid positions, this is positive. end record; -- Record to represent position in a given text type Token_Kind is (J_NULL, J_TRUE, J_FALSE, J_NUMBER, J_INTEGER, J_STRING, J_ARRAY, J_OBJECT, J_ARRAY_END, J_OBJECT_END, J_COMMA, J_COLON, J_EOF); -- JSON Token kinds. Note that in ECMA 404 there is no notion of integer. -- Only numbers are supported. In our implementation we return J_INTEGER -- if there is no decimal part in the number. The semantic is that this is -- a J_NUMBER token that "might" be represented as an integer. Special -- token J_EOF means that end of stream has been reached. subtype Value_Token is Token_Kind range J_NULL .. J_OBJECT; -- Subset of token kinds for JSON values: null, false, true, a string, a -- number, an array or an object. procedure Free is new Ada.Unchecked_Deallocation (JSON_Array_Internal, JSON_Array_Access); procedure Free is new Ada.Unchecked_Deallocation (JSON_Object_Internal, JSON_Object_Access); procedure Write (Item : JSON_Value; Compact : Boolean; Indent : Natural; Ret : in out Unbounded_String); -- Auxiliary write function procedure Read (Result : in out Read_Result; Strm : String; Pos : in out Text_Position; Kind : out Token_Kind; Check_EOF : Boolean := False) with Pre => Result.Success; -- Read and decode a JSON value. On success, this returns a Read_Result -- with Success => True, otherwise it returns an error with a Success => -- False record. -- -- If Check_EOF is true, return an error if we haven't reached the end of -- the input string upon returning. If Check_EOF if false and no value -- could be decoded but we still managed to read a token, just skip this -- token: in that case, a null JSON value is returned. -- -- Strm is the content to decode, -- -- Pos is the position in Strm from which we start reading. It is updated -- to point past that token. -- -- Kind is set to the last read token kind, -- -- Note that we use an IN OUT parameter instead of a mere return value for -- the result to avoid a noticeable runtime penalty, probably due to -- the secondary stack management involved. type Read_Token_Result (Success : Boolean := True) is record case Success is when True => Kind : Token_Kind; when False => Error : Parsing_Error; end case; end record; function Read_Token (Strm : String; Pos : in out Text_Position; Token_Start : out Text_Position; Token_End : out Text_Position) return Read_Token_Result; -- Read a token. -- -- Strm is the content to decode, -- -- Pos is the position in Strm from which the token is read. It is updated -- to point past that token. -- -- Token_Start are Token_End are set respectively to the position of the -- first and last character of the token (outside boundaries of Strm if the -- return token is J_EOF). -- -- If a parsing error is detected, this returns a Read_Token_Error record -- with Success => False, including the relevant parsing error information. ------------ -- Append -- ------------ procedure Append (Arr : JSON_Value; Item : JSON_Value) is begin Append (Arr.Data.Arr_Value.Arr, Item); end Append; -------------- -- Is_Empty -- -------------- function Is_Empty (Val : JSON_Value) return Boolean is begin case Val.Kind is when JSON_Null_Type => return True; when JSON_Array_Type => return Val.Data.Arr_Value.Arr.Vals.Is_Empty; when JSON_Object_Type => return Val.Data.Obj_Value.Vals.Is_Empty; when others => return False; end case; end Is_Empty; ----------------- -- Array_First -- ----------------- function Array_First (Arr : JSON_Array) return Positive is pragma Unreferenced (Arr); begin return 1; end Array_First; ---------------- -- Array_Next -- ---------------- function Array_Next (Arr : JSON_Array; Index : Positive) return Positive is pragma Unreferenced (Arr); begin return Index + 1; end Array_Next; ----------------------- -- Array_Has_Element -- ----------------------- function Array_Has_Element (Arr : JSON_Array; Index : Positive) return Boolean is begin return Index <= Length (Arr); end Array_Has_Element; ------------------- -- Array_Element -- ------------------- function Array_Element (Arr : JSON_Array; Index : Positive) return JSON_Value is begin return Get (Arr, Index); end Array_Element; -------------------------- -- Format_Parsing_Error -- -------------------------- function Format_Parsing_Error (Error : Parsing_Error) return String is L : constant String := Error.Line'Img; C : constant String := Error.Column'Img; begin return (L (L'First + 1 .. L'Last) & ":" & C (C'First + 1 .. C'Last) & ": " & To_String (Error.Message)); end Format_Parsing_Error; ---------------- -- Read_Token -- ---------------- function Read_Token (Strm : String; Pos : in out Text_Position; Token_Start : out Text_Position; Token_End : out Text_Position) return Read_Token_Result is procedure Next_Char; -- Update Pos to point to next character in Strm function Next_Char (Result : Token_Kind) return Read_Token_Result with Inline; -- Shortcut to call the Next_Char procedure after returning Result function Is_Whitespace return Boolean with Inline; -- Return True of current character is a whitespace: line feed, carriage -- return, space or horizontal tabulation. function Is_Structural_Token return Boolean with Inline; -- Return True if current character is one of the structural tokens: -- brackets, parens, comma or colon. function Is_Token_Sep return Boolean with Inline; -- Return True if at least of of the following is true: -- -- * we reached the end of input string; -- * the current character is a whitespace; -- * the current character is a structural token. function Error (Msg : String) return Read_Token_Result; -- Return a parsing error for the current position and the given error -- message. function Delimit_Keyword (Kw : String; Kind : Token_Kind) return Read_Token_Result; -- Advance Pos until we reach the next token separator, updating -- Token_End to designate the last character. Return the resulting token -- if it matches Kw/Kind, otherwise raise an error. ----------- -- Error -- ----------- function Error (Msg : String) return Read_Token_Result is begin return (Success => False, Error => (Line => Pos.Line, Column => Pos.Column, Message => To_Unbounded_String (Msg))); end Error; --------------- -- Next_Char -- --------------- procedure Next_Char is begin Pos.Index := Pos.Index + 1; if Pos.Index > Strm'Last then Pos.Column := Pos.Column + 1; elsif Strm (Pos.Index) = ASCII.LF then Pos.Column := 1; Pos.Line := Pos.Line + 1; else Pos.Column := Pos.Column + 1; end if; end Next_Char; function Next_Char (Result : Token_Kind) return Read_Token_Result is begin Next_Char; return (Success => True, Kind => Result); end Next_Char; ------------------- -- Is_Whitespace -- ------------------- function Is_Whitespace return Boolean is begin return (Pos.Index <= Strm'Last and then Strm (Pos.Index) in ASCII.LF | ASCII.CR | ASCII.HT | ' '); end Is_Whitespace; ------------------------- -- Is_Structural_Token -- ------------------------- function Is_Structural_Token return Boolean is begin return (Pos.Index <= Strm'Last and then Strm (Pos.Index) in '[' | ']' | '{' | '}' | ',' | ':'); end Is_Structural_Token; ------------------ -- Is_Token_Sep -- ------------------ function Is_Token_Sep return Boolean is begin return (Pos.Index > Strm'Last or else Is_Whitespace or else Is_Structural_Token); end Is_Token_Sep; --------------------- -- Delimit_Keyword -- --------------------- function Delimit_Keyword (Kw : String; Kind : Token_Kind) return Read_Token_Result is begin while not Is_Token_Sep loop Token_End := Pos; Next_Char; end loop; if Strm (Token_Start.Index .. Token_End.Index) /= Kw then return Error ("invalid keyword starting with: " & Strm (Token_Start.Index .. Token_End.Index)); else return (Success => True, Kind => Kind); end if; end Delimit_Keyword; CC : Character; -- Buffer for the currently analyzed character Can_Be_Integer : Boolean := True; -- When reading a number token, this is true if that number can be an -- integer: otherwise, it must be interpreted as a decimal number. begin -- Skip leading whitespaces while Is_Whitespace loop Next_Char; end loop; -- Initialize token delimiters Token_Start := Pos; Token_End := Pos; -- If we reached the end of the input string, just return a EOF token if Pos.Index > Strm'Last then return (Success => True, Kind => J_EOF); end if; -- Otherwise, all depends on the first non-whitespace character to read -- next... CC := Strm (Pos.Index); case CC is -- Structual tokens are unambiguously designated by standalone -- characters. when '[' => return Next_Char (J_ARRAY); when ']' => return Next_Char (J_ARRAY_END); when '{' => return Next_Char (J_OBJECT); when '}' => return Next_Char (J_OBJECT_END); when ',' => return Next_Char (J_COMMA); when ':' => return Next_Char (J_COLON); -- Only named value tokens can start with a letter when 'n' => return Delimit_Keyword ("null", J_NULL); when 'f' => return Delimit_Keyword ("false", J_FALSE); when 't' => return Delimit_Keyword ("true", J_TRUE); when '"' => -- We expect a string. -- -- Just scan till the end the of the string but do not attempt to -- decode it. This means that even if we get a string token it might -- not be a valid string from the ECMA 404 point of view. Next_Char; while Pos.Index <= Strm'Last and then Strm (Pos.Index) /= '"' loop CC := Strm (Pos.Index); if CC in ASCII.NUL .. ASCII.US then return Error ("control character not allowed in string"); end if; if CC = '\' then -- This is an escape sequence. Make sure we have at least one -- more character to read. Next_Char; if Pos.Index > Strm'Last then return Error ("non terminated string"); end if; case Strm (Pos.Index) is when 'u' => -- This is a unicode escape sequence ("\uXXXX") for Idx in 1 .. 4 loop Next_Char; if Pos.Index > Strm'Last then return Error ("non terminated string"); elsif Strm (Pos.Index) not in 'a' .. 'f' | 'A' .. 'F' | '0' .. '9' then return Error ("invalid unicode escape sequence"); end if; end loop; when '\' | '/' | '"' | 'b' | 'f' | 'n' | 'r' | 't' => -- This is a single-character escape sequence null; when others => return Error ("invalid escape sequence"); end case; end if; Next_Char; end loop; -- We could not find a closing quote before the end of the input -- string: this is an error. if Pos.Index > Strm'Last then return Error ("non terminated string"); end if; Token_End := Pos; -- Go to next char and ensure that this is separator. Indeed, -- construction such as "string1""string2" are not allowed. Next_Char; if not Is_Token_Sep then return Error ("invalid syntax"); end if; return (Success => True, Kind => J_STRING); when '-' | '0' .. '9' => -- We expect a number. If it's a negative one, just discard the -- leading dash. if CC = '-' then Next_Char; if Pos.Index > Strm'Last then return Error ("invalid number"); end if; end if; -- Parse the integer part of a number. Leading zeros (except a mere -- "0" of course) are not allowed. case Strm (Pos.Index) is when '0' => Token_End := Pos; Next_Char; when '1' .. '9' => Token_End := Pos; Next_Char; while Pos.Index <= Strm'Last and then Strm (Pos.Index) in '0' .. '9' loop Token_End := Pos; Next_Char; end loop; when others => return Error ("invalid number"); end case; if Is_Token_Sep then -- The token stops here, so we have a valid integer number return (Success => True, Kind => J_INTEGER); elsif Strm (Pos.Index) not in '.' | 'e' | 'E' then -- At this point, we allow only an exponent or a decimal number return Error ("invalid number"); end if; -- If present, handle the decimals if Strm (Pos.Index) = '.' then Can_Be_Integer := False; Token_End := Pos; Next_Char; if Pos.Index > Strm'Last or else Strm (Pos.Index) not in '0' .. '9' then return Error ("invalid number"); end if; while Pos.Index <= Strm'Last and then Strm (Pos.Index) in '0' .. '9' loop Token_End := Pos; Next_Char; end loop; end if; -- If present, handle the exponent if Pos.Index <= Strm'Last and then Strm (Pos.Index) in 'e' | 'E' then Token_End := Pos; Next_Char; if Pos.Index > Strm'Last then return Error ("invalid number"); end if; -- Skip the sign, if present case Strm (Pos.Index) is when '-' => -- The exponent is negative. Even though several corner -- cases (such as having "1" as the prefix) can lead to an -- integer, assume that the number is not an integer. Can_Be_Integer := False; Next_Char; when '+' => Next_Char; when others => null; end case; if Pos.Index > Strm'Last or else Strm (Pos.Index) not in '0' .. '9' then return Error ("invalid number"); end if; while Pos.Index <= Strm'Last and then Strm (Pos.Index) in '0' .. '9' loop Token_End := Pos; Next_Char; end loop; end if; if Is_Token_Sep then -- The token stops here, so we have a valid integer number return (Success => True, Kind => (if Can_Be_Integer then J_INTEGER else J_NUMBER)); else return Error ("invalid number"); end if; when others => return Error ("Unexpected character '" & CC & '''); end case; end Read_Token; ---------- -- Read -- ---------- procedure Read (Result : in out Read_Result; Strm : String; Pos : in out Text_Position; Kind : out Token_Kind; Check_EOF : Boolean := False) is function Error (Msg : String) return Read_Result; -- Return a parsing error for the current position and the given error -- message. function Error (Result : Read_Token_Result) return Read_Result with Pre => not Result.Success; -- Transform a parsing error from Read_Token into a Read_Result ----------- -- Error -- ----------- function Error (Msg : String) return Read_Result is begin return (Success => False, Error => (Line => Pos.Line, Column => Pos.Column, Message => To_Unbounded_String (Msg))); end Error; function Error (Result : Read_Token_Result) return Read_Result is begin return (Success => False, Error => Result.Error); end Error; Token_Start, Token_End : Text_Position; -- Boundaries for the currently analyzed token Token_Result : Read_Token_Result; -- Buffer for token reads begin -- The first token we get determines the kind of JSON value to return Token_Result := Read_Token (Strm, Pos, Token_Start, Token_End); if not Token_Result.Success then Result := Error (Token_Result); Kind := J_EOF; return; end if; Kind := Token_Result.Kind; case Kind is when J_EOF => Result := Error ("empty stream"); return; when J_NULL => Result.Value := Create; when J_FALSE => Result.Value := Create (False); when J_TRUE => Result.Value := Create (True); when J_STRING => begin declare Str_Value : constant UTF8_XString := Utility.Un_Escape_String (Strm, Token_Start.Index, Token_End.Index); begin Result.Value := Create (Str_Value); end; exception when Exc : Invalid_JSON_Stream => Result := Error (Ada.Exceptions.Exception_Message (Exc)); return; end; when J_ARRAY => declare -- In order to avoid the costly array copy in Create -- (JSON_Array), we use an aggregate below in order to build -- the result, so directly allocate the JSON_Array_Access here. Arr : JSON_Array_Access := new JSON_Array_Internal; ST : Token_Kind; -- Buffer for the kind of tokens we read Element : Read_Result; -- Buffer for the JSON values that constitute array elements Is_First : Boolean := True; -- True if we are still reading the first array element. False -- afterwards. begin -- Read all elements in the array until we reach the closing -- token ("]"). loop Read (Element, Strm, Pos, ST); if not Element.Success then Free (Arr); Result := Element; return; end if; case ST is when J_ARRAY_END => exit when Is_First; Free (Arr); Result := Error ("syntax error"); return; when Value_Token => -- We got a new array element: append it Append (Arr.Arr, Element.Value); -- Now see what is next: the end of the array or a comma -- (hence another array element after). Read (Element, Strm, Pos, ST); if not Element.Success then Free (Arr); Result := Element; return; end if; case ST is when J_ARRAY_END => exit; when J_COMMA => -- We have a comma, so we expect another element in -- the array. Continue reading. null; when others => Free (Arr); Result := Error ("comma expected"); return; end case; when others => Free (Arr); Result := Error ("syntax error"); return; end case; Is_First := False; end loop; Result.Value := (Ada.Finalization.Controlled with Data => (Kind => JSON_Array_Type, Arr_Value => Arr)); end; when J_OBJECT => declare Is_First : Boolean := True; -- True if we are still reading the first object member. False -- afterwards. ST : Token_Kind; -- Buffer for the kind of tokens we read Key, Value : Read_Result; -- Buffer for the JSON values that constitute keys and member -- values. begin -- Allocate internal container for the result Result.Value := (Ada.Finalization.Controlled with Data => (Kind => JSON_Object_Type, Obj_Value => new JSON_Object_Internal)); -- Read all members for this object until we reach the closing -- token ("}"). loop -- First try to read the key for the next member Read (Key, Strm, Pos, ST); if not Key.Success then Result := Key; return; end if; case ST is when J_OBJECT_END => exit when Is_First; Result := Error ("string value expected"); return; when J_STRING => -- Consume the colon token, then get the member value Read (Value, Strm, Pos, ST); if not Value.Success then Result := Value; return; elsif ST /= J_COLON then Result := Error ("colon expected"); return; end if; Read (Value, Strm, Pos, ST); if not Value.Success then Result := Value; return; elsif ST not in Value_Token then Result := Error ("non expected token"); return; end if; -- Register this new member. -- -- As we checked above that reading Key parsed a string -- token, we know that coercing Key to a string cannot -- fail. declare Key_Str : constant UTF8_XString := Get (Key.Value); begin Set_Field (Result.Value, Key_Str, Value.Value); end; -- Now see what is next: the end of the object or a comma -- (hence another object member after). Read (Value, Strm, Pos, ST); if not Value.Success then Result := Value; return; end if; case ST is when J_OBJECT_END => exit; when J_COMMA => -- We have a comma, so we expect another member in the -- object. Continue reading. null; when others => Result := Error ("comma expected"); return; end case; when others => Result := Error ("string value expected"); return; end case; Is_First := False; end loop; end; when J_NUMBER | J_INTEGER => declare Number_Str : constant String := Strm (Token_Start.Index .. Token_End.Index); Has_Integer : Boolean := False; begin if Kind = J_INTEGER then declare Result_Int : Long_Long_Integer; begin Result_Int := Long_Long_Integer'Value (Number_Str); Result.Value := Create (Result_Int); Has_Integer := True; exception when Constraint_Error => null; end; end if; if not Has_Integer then begin Result.Value := Create (Long_Float'Value (Number_Str)); exception when Constraint_Error => Result := Error ("cannot convert JSON number to Long_Float"); return; end; end if; end; when others => if Check_EOF then Result := Error ("invalid JSON stream"); return; else Result.Value := Create; end if; end case; if Check_EOF then Token_Result := Read_Token (Strm, Pos, Token_Start, Token_End); if not Token_Result.Success or else Token_Result.Kind /= J_EOF then Result := Error ("additional data after end of JSON stream"); return; end if; end if; end Read; function Read (Strm : Unbounded_String; Filename : String := "") return JSON_Value is begin return Read (To_String (Strm), Filename); end Read; function Read (Strm : String; Filename : String := "") return JSON_Value is Result : constant Read_Result := Read (Strm); begin if Result.Success then return Result.Value; else Ada.Text_IO.New_Line; if Filename = "" then Ada.Text_IO.Put (":"); else Ada.Text_IO.Put (Filename & ":"); end if; Ada.Text_IO.Put_Line (Format_Parsing_Error (Result.Error)); raise Invalid_JSON_Stream with To_String (Result.Error.Message); end if; end Read; function Read (Strm : Ada.Strings.Unbounded.Unbounded_String) return Read_Result is begin return Read (To_String (Strm)); end Read; function Read (Strm : String) return Read_Result is Pos : Text_Position := (Strm'First, 1, 1); Kind : Token_Kind; Result : Read_Result := (Success => True, others => <>); begin Read (Result, Strm, Pos, Kind, Check_EOF => True); return Result; end Read; ----------- -- Write -- ----------- procedure Write (Item : JSON_Value; Compact : Boolean; Indent : Natural; Ret : in out Unbounded_String) is procedure Do_Indent (Val : Natural); -- Adds whitespace characters to Ret corresponding to the indentation -- level. --------------- -- Do_Indent -- --------------- procedure Do_Indent (Val : Natural) is begin if Compact then return; end if; Append (Ret, (1 .. 2 * Val => ' ')); end Do_Indent; begin case Item.Kind is when JSON_Null_Type => Append (Ret, "null"); when JSON_Boolean_Type => if Item.Data.Bool_Value then Append (Ret, "true"); else Append (Ret, "false"); end if; when JSON_Int_Type => declare S : constant String := Item.Data.Int_Value'Img; begin if S (S'First) = ' ' then Append (Ret, S (S'First + 1 .. S'Last)); else Append (Ret, S); end if; end; when JSON_Float_Type => declare S : constant String := Item.Data.Flt_Value'Img; begin if S (S'First) = ' ' then Append (Ret, S (S'First + 1 .. S'Last)); else Append (Ret, S); end if; end; when JSON_String_Type => Append (Ret, JSON.Utility.Escape_String (Item.Data.Str_Value)); when JSON_Array_Type => Append (Ret, '['); if not Compact then Append (Ret, ASCII.LF); end if; for J in Item.Data.Arr_Value.Arr.Vals.First_Index .. Item.Data.Arr_Value.Arr.Vals.Last_Index loop Do_Indent (Indent + 1); Write (Item.Data.Arr_Value.Arr.Vals.Element (J), Compact, Indent + 1, Ret); if J < Item.Data.Arr_Value.Arr.Vals.Last_Index then Append (Ret, ","); end if; if not Compact then Append (Ret, ASCII.LF); end if; end loop; Do_Indent (Indent); Append (Ret, ']'); when JSON_Object_Type => declare use Object_Items_Pkg; J : Object_Items_Pkg.Cursor := Item.Data.Obj_Value.Vals.First; begin Append (Ret, '{'); if not Compact then Append (Ret, ASCII.LF); end if; while Has_Element (J) loop Do_Indent (Indent + 1); Append (Ret, GNATCOLL.JSON.Utility.Escape_String (Element (J).Key)); Append (Ret, ':'); if not Compact then Append (Ret, ' '); end if; Write (Element (J).Val, Compact, Indent + 1, Ret); Next (J); if Has_Element (J) then Append (Ret, ","); end if; if not Compact then Append (Ret, ASCII.LF); end if; end loop; Do_Indent (Indent); Append (Ret, '}'); end; end case; end Write; ----------- -- Write -- ----------- function Write (Item : JSON_Value; Compact : Boolean := True) return String is begin return To_String (Write (Item, Compact)); end Write; ----------- -- Write -- ----------- function Write (Item : JSON_Value; Compact : Boolean := True) return Unbounded_String is Ret : Unbounded_String; begin Write (Item, Compact, 0, Ret); return Ret; end Write; ------------ -- Length -- ------------ function Length (Arr : JSON_Array) return Natural is begin return Natural (Arr.Vals.Length); end Length; -------------- -- Is_Empty -- -------------- function Is_Empty (Arr : JSON_Array) return Boolean is begin return Arr.Vals.Is_Empty; end Is_Empty; --------- -- Get -- --------- function Get (Arr : JSON_Array; Index : Positive) return JSON_Value is begin return Arr.Vals.Element (Index); end Get; ----------------- -- Set_Element -- ----------------- procedure Set_Element (Arr : in out JSON_Array; Index : Positive; Item : JSON_Value) is begin Arr.Vals.Replace_Element (Index, Item); end Set_Element; ---------- -- Sort -- ---------- procedure Sort (Arr : in out JSON_Array; Less : access function (Left, Right : JSON_Value) return Boolean) is package Sorting is new Vect_Pkg.Generic_Sorting ("<" => Less.all); begin Sorting.Sort (Arr.Vals); end Sort; procedure Sort (Val : in out JSON_Value; Less : access function (Left, Right : JSON_Value) return Boolean) is function "<" (Left, Right : Object_Item) return Boolean; function "<" (Left, Right : Object_Item) return Boolean is begin return Less (Left.Val, Right.Val); end "<"; package Sorting is new Object_Items_Pkg.Generic_Sorting ("<"); begin case Val.Kind is when JSON_Array_Type => Sort (Val.Data.Arr_Value.Arr, Less); when JSON_Object_Type => Sorting.Sort (Val.Data.Obj_Value.Vals); when others => null; end case; end Sort; ------------ -- Append -- ------------ procedure Append (Arr : in out JSON_Array; Val : JSON_Value) is begin Arr.Vals.Append (Val); end Append; ------------- -- Prepend -- ------------- procedure Prepend (Arr : in out JSON_Array; Val : JSON_Value) is begin Arr.Vals.Prepend (Val); end Prepend; --------- -- "&" -- --------- function "&" (Arr : JSON_Array; Value : JSON_Value) return JSON_Array is Result : JSON_Array := Arr; begin Append (Result, Value); return Result; end "&"; function "&" (Value1, Value2 : JSON_Value) return JSON_Array is Result : JSON_Array; begin Append (Result, Value1); Append (Result, Value2); return Result; end "&"; ----------- -- Clear -- ----------- procedure Clear (Arr : in out JSON_Array) is begin Arr.Vals.Clear; end Clear; ------------ -- Adjust -- ------------ overriding procedure Adjust (Obj : in out JSON_Value) is begin case Obj.Data.Kind is when JSON_Array_Type => if Obj.Data.Arr_Value /= null then Increment (Obj.Data.Arr_Value.Cnt); end if; when JSON_Object_Type => if Obj.Data.Obj_Value /= null then Increment (Obj.Data.Obj_Value.Cnt); end if; when others => null; end case; end Adjust; -------------- -- Finalize -- -------------- overriding procedure Finalize (Obj : in out JSON_Value) is begin case Obj.Data.Kind is when JSON_Array_Type => declare Arr : JSON_Array_Access := Obj.Data.Arr_Value; begin Obj.Data.Arr_Value := null; if Arr /= null and then Decrement (Arr.Cnt) then Free (Arr); end if; end; when JSON_Object_Type => declare Object : JSON_Object_Access := Obj.Data.Obj_Value; begin Obj.Data.Obj_Value := null; if Object /= null and then Decrement (Object.Cnt) then Free (Object); end if; end; when others => null; end case; end Finalize; ------------ -- Create -- ------------ function Create return JSON_Value is begin return JSON_Null; end Create; function Create (Val : Boolean) return JSON_Value is Ret : JSON_Value; begin Ret.Data := (Kind => JSON_Boolean_Type, Bool_Value => Val); return Ret; end Create; function Create (Val : Integer) return JSON_Value is Ret : JSON_Value; begin Ret.Data := (JSON_Int_Type, Int_Value => Long_Long_Integer (Val)); return Ret; end Create; function Create (Val : Long_Integer) return JSON_Value is Ret : JSON_Value; begin Ret.Data := (JSON_Int_Type, Int_Value => Long_Long_Integer (Val)); return Ret; end Create; function Create (Val : Long_Long_Integer) return JSON_Value is Ret : JSON_Value; begin Ret.Data := (Kind => JSON_Int_Type, Int_Value => Val); return Ret; end Create; function Create (Val : Float) return JSON_Value is Ret : JSON_Value; begin Ret.Data := (Kind => JSON_Float_Type, Flt_Value => Long_Float (Val)); return Ret; end Create; function Create (Val : Long_Float) return JSON_Value is Ret : JSON_Value; begin Ret.Data := (Kind => JSON_Float_Type, Flt_Value => Val); return Ret; end Create; function Create (Val : UTF8_String) return JSON_Value is Ret : JSON_Value; begin Ret.Data := (JSON_String_Type, Str_Value => <>); Ret.Data.Str_Value.Set (Val); return Ret; end Create; function Create (Val : UTF8_Unbounded_String) return JSON_Value is Ret : JSON_Value; begin Ret.Data := (Kind => JSON_String_Type, Str_Value => Null_XString); Ret.Data.Str_Value.Set (To_String (Val)); return Ret; end Create; function Create (Val : UTF8_XString) return JSON_Value is Ret : JSON_Value; begin Ret.Data := (Kind => JSON_String_Type, Str_Value => Val); return Ret; end Create; function Create (Val : JSON_Array) return JSON_Value is begin return (Ada.Finalization.Controlled with Data => (Kind => JSON_Array_Type, Arr_Value => new JSON_Array_Internal' (Cnt => 1, Arr => Val))); end Create; ------------------- -- Create_Object -- ------------------- function Create_Object return JSON_Value is Ret : JSON_Value; begin Ret.Data := (JSON_Object_Type, Obj_Value => new JSON_Object_Internal); return Ret; end Create_Object; ----------------- -- Unset_Field -- ----------------- procedure Unset_Field (Val : JSON_Value; Field_Name : UTF8_String) is Vals : Object_Items_Pkg.Vector renames Val.Data.Obj_Value.Vals; begin for J in Vals.First_Index .. Vals.Last_Index loop if Vals.Element (J).Key = Field_Name then Val.Data.Obj_Value.Vals.Delete (J); return; end if; end loop; end Unset_Field; --------------- -- Set_Field -- --------------- procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : JSON_Value) is Vals : Object_Items_Pkg.Vector renames Val.Data.Obj_Value.Vals; begin for J in Vals.First_Index .. Vals.Last_Index loop if Field_Name = Vals.Element (J).Key then Vals.Replace_Element (J, (Vals.Element (J).Key, Field)); return; end if; end loop; Vals.Append ((Key => To_XString (Field_Name), Val => Field)); end Set_Field; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_XString; Field : JSON_Value) is Vals : Object_Items_Pkg.Vector renames Val.Data.Obj_Value.Vals; begin for J in Vals.First_Index .. Vals.Last_Index loop if Field_Name = Vals.Element (J).Key then Vals.Replace_Element (J, (Field_Name, Field)); return; end if; end loop; Vals.Append ((Key => Field_Name, Val => Field)); end Set_Field; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : Boolean) is begin Set_Field (Val, Field_Name, Create (Field)); end Set_Field; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : Integer) is begin Set_Field (Val, Field_Name, Create (Field)); end Set_Field; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : Long_Integer) is begin Set_Field (Val, Field_Name, Create (Field)); end Set_Field; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : Float) is begin Set_Field (Val, Field_Name, Create (Field)); end Set_Field; procedure Set_Field_Long_Float (Val : JSON_Value; Field_Name : UTF8_String; Field : Long_Float) is begin Set_Field (Val, Field_Name, Create (Field)); end Set_Field_Long_Float; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : UTF8_String) is begin Set_Field (Val, Field_Name, Create (Field)); end Set_Field; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : UTF8_Unbounded_String) is begin Set_Field (Val, Field_Name, Create (Field)); end Set_Field; procedure Set_Field (Val : JSON_Value; Field_Name : UTF8_String; Field : JSON_Array) is F_Val : constant JSON_Value := Create (Field); begin Set_Field (Val, Field_Name, F_Val); end Set_Field; ---------------------------- -- Set_Field_If_Not_Empty -- ---------------------------- procedure Set_Field_If_Not_Empty (Val : JSON_Value; Field_Name : UTF8_String; Field : UTF8_Unbounded_String) is begin if Field /= Null_Unbounded_String then Set_Field (Val, Field_Name, Field); end if; end Set_Field_If_Not_Empty; procedure Set_Field_If_Not_Empty (Val : JSON_Value; Field_Name : UTF8_String; Field : UTF8_String) is begin if Field /= "" then Set_Field (Val, Field_Name, Field); end if; end Set_Field_If_Not_Empty; procedure Set_Field_If_Not_Empty (Val : JSON_Value; Field_Name : UTF8_String; Field : JSON_Array) is begin if Field /= Empty_Array then Set_Field (Val, Field_Name, Field); end if; end Set_Field_If_Not_Empty; ---------- -- Kind -- ---------- function Kind (Val : JSON_Value) return JSON_Value_Type is begin return Val.Data.Kind; end Kind; --------- -- Get -- --------- function Get (Val : JSON_Value) return Boolean is begin return Val.Data.Bool_Value; end Get; function Get (Val : JSON_Value) return Integer is begin return Integer (Val.Data.Int_Value); end Get; function Get (Val : JSON_Value) return Long_Integer is begin return Long_Integer (Val.Data.Int_Value); end Get; function Get (Val : JSON_Value) return Long_Long_Integer is begin return Val.Data.Int_Value; end Get; function Get (Val : JSON_Value) return Float is begin return Float (Val.Data.Flt_Value); end Get; function Get_Long_Float (Val : JSON_Value) return Long_Float is begin return Val.Data.Flt_Value; end Get_Long_Float; function Get (Val : JSON_Value) return UTF8_String is begin return To_String (Val.Data.Str_Value); end Get; function Get (Val : JSON_Value) return UTF8_XString is begin return Val.Data.Str_Value; end Get; function Get (Val : JSON_Value) return UTF8_Unbounded_String is begin return To_Unbounded_String (Val.Data.Str_Value.To_String); end Get; function Get (Val : JSON_Value; Field : UTF8_String) return JSON_Value is Vals : Object_Items_Pkg.Vector renames Val.Data.Obj_Value.Vals; begin for J in Vals.First_Index .. Vals.Last_Index loop if Field = Vals.Element (J).Key then return Vals.Element (J).Val; end if; end loop; return JSON_Null; end Get; function Get (Val : JSON_Value) return JSON_Array is begin return Val.Data.Arr_Value.Arr; end Get; --------------- -- Has_Field -- --------------- function Has_Field (Val : JSON_Value; Field : UTF8_String) return Boolean is Vals : Object_Items_Pkg.Vector renames Val.Data.Obj_Value.Vals; begin for J in Vals.First_Index .. Vals.Last_Index loop if Field = Vals.Element (J).Key then return True; end if; end loop; return False; end Has_Field; --------- -- Get -- --------- function Get (Val : JSON_Value; Field : UTF8_String) return Boolean is begin return Get (Get (Val, Field)); end Get; function Get (Val : JSON_Value; Field : UTF8_String) return Integer is begin return Get (Get (Val, Field)); end Get; function Get (Val : JSON_Value; Field : UTF8_String) return Long_Integer is begin return Get (Get (Val, Field)); end Get; function Get (Val : JSON_Value; Field : UTF8_String) return Float is begin return Get (Get (Val, Field)); end Get; function Get_Long_Float (Val : JSON_Value; Field : UTF8_String) return Long_Float is begin return Get_Long_Float (Get (Val, Field)); end Get_Long_Float; function Get (Val : JSON_Value; Field : UTF8_String) return UTF8_String is begin return Get (Get (Val, Field)); end Get; function Get (Val : JSON_Value; Field : UTF8_String) return UTF8_Unbounded_String is begin return Get (Get (Val, Field)); end Get; function Get (Val : JSON_Value; Field : UTF8_String) return JSON_Array is begin return Get (Get (Val, Field)); end Get; ----------- -- Clone -- ----------- function Clone (Val : JSON_Value) return JSON_Value is begin case Val.Data.Kind is when JSON_Null_Type => return JSON_Null; when JSON_Boolean_Type => return Create (Val.Data.Bool_Value); when JSON_Int_Type => return Create (Val.Data.Int_Value); when JSON_Float_Type => return Create (Val.Data.Flt_Value); when JSON_String_Type => return Create (Val.Data.Str_Value); when JSON_Array_Type => declare Result : constant JSON_Value := (Ada.Finalization.Controlled with Data => (Kind => JSON_Array_Type, Arr_Value => new JSON_Array_Internal)); begin for E of Val.Data.Arr_Value.Arr.Vals loop Append (Result.Data.Arr_Value.Arr, Clone (E)); end loop; return Result; end; when JSON_Object_Type => declare Result : constant JSON_Value := Create_Object; begin for E of Val.Data.Obj_Value.Vals loop Result.Set_Field (To_String (E.Key), Clone (E.Val)); end loop; return Result; end; end case; end Clone; --------- -- "=" -- --------- function "=" (Left, Right : JSON_Value) return Boolean is Found : Boolean; begin if Left.Data.Kind /= Right.Data.Kind then return False; end if; case Left.Data.Kind is when JSON_Null_Type => return True; when JSON_Boolean_Type => return Left.Data.Bool_Value = Right.Data.Bool_Value; when JSON_Int_Type => return Left.Data.Int_Value = Right.Data.Int_Value; when JSON_Float_Type => return Left.Data.Flt_Value = Right.Data.Flt_Value; when JSON_String_Type => return Left.Data.Str_Value = Right.Data.Str_Value; when JSON_Array_Type => -- Same pointer ? if Left.Data.Arr_Value = Right.Data.Arr_Value then return True; elsif Left.Data.Arr_Value.Arr.Vals.Length /= Right.Data.Arr_Value.Arr.Vals.Length then return False; else for J in Left.Data.Arr_Value.Arr.Vals.First_Index .. Left.Data.Arr_Value.Arr.Vals.Last_Index loop if not (Left.Data.Arr_Value.Arr.Vals (J) = -- recursive Right.Data.Arr_Value.Arr.Vals (J)) then return False; end if; end loop; return True; end if; when JSON_Object_Type => -- Same pointer ? if Left.Data.Obj_Value = Right.Data.Obj_Value then return True; elsif Left.Data.Obj_Value.Vals.Length /= Right.Data.Obj_Value.Vals.Length then return False; else -- We have the same number of elements, and no duplicates for L of Left.Data.Obj_Value.Vals loop Found := False; for R of Right.Data.Obj_Value.Vals loop if R.Key = L.Key then if not (R.Val = L.Val) then -- recursive return False; end if; Found := True; exit; end if; end loop; if not Found then return False; end if; end loop; return True; end if; end case; end "="; --------------------- -- Map_JSON_Object -- --------------------- procedure Map_JSON_Object (Val : JSON_Value; CB : access procedure (Name : UTF8_String; Value : JSON_Value)) is Vals : Object_Items_Pkg.Vector renames Val.Data.Obj_Value.Vals; begin for J in Vals.First_Index .. Vals.Last_Index loop CB (To_String (Vals.Element (J).Key), Vals.Element (J).Val); end loop; end Map_JSON_Object; --------------------- -- Map_JSON_Object -- --------------------- procedure Gen_Map_JSON_Object (Val : JSON_Value; CB : access procedure (User_Object : in out Mapped; Name : UTF8_String; Value : JSON_Value); User_Object : in out Mapped) is procedure Internal (Name : UTF8_String; Value : JSON_Value); -------------- -- Internal -- -------------- procedure Internal (Name : UTF8_String; Value : JSON_Value) is begin CB (User_Object, Name, Value); end Internal; begin Map_JSON_Object (Val, Internal'Access); end Gen_Map_JSON_Object; end GNATCOLL.JSON; gnatcoll-core-21.0.0/src/gnatcoll-tribooleans.adb0000644000175000017500000002117713661715457021575 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package body GNATCOLL.Tribooleans is And_Truth_Table1 : constant array (Triboolean, Triboolean) of Triboolean := (True => (True => True, False => False, Indeterminate => Indeterminate), False => (True => False, False => False, Indeterminate => False), Indeterminate => (True => Indeterminate, False => False, Indeterminate => Indeterminate)); And_Truth_Table2 : constant array (Triboolean, Boolean) of Triboolean := (True => (True => True, False => False), False => (True => False, False => False), Indeterminate => (True => Indeterminate, False => False)); Or_Truth_Table1 : constant array (Triboolean, Triboolean) of Triboolean := (True => (True => True, False => True, Indeterminate => True), False => (True => True, False => False, Indeterminate => Indeterminate), Indeterminate => (True => True, False => Indeterminate, Indeterminate => Indeterminate)); Or_Truth_Table2 : constant array (Triboolean, Boolean) of Triboolean := (True => (True => True, False => True), False => (True => True, False => False), Indeterminate => (True => True, False => Indeterminate)); Xor_Truth_Table1 : constant array (Triboolean, Triboolean) of Triboolean := (True => (True => False, False => True, Indeterminate => Indeterminate), False => (True => True, False => False, Indeterminate => Indeterminate), Indeterminate => (True => Indeterminate, False => Indeterminate, Indeterminate => Indeterminate)); Xor_Truth_Table2 : constant array (Triboolean, Boolean) of Triboolean := (True => (True => False, False => True), False => (True => True, False => False), Indeterminate => (True => Indeterminate, False => Indeterminate)); Eq_Truth_Table1 : constant array (Triboolean, Triboolean) of Triboolean := (True => (True => True, False => False, Indeterminate => Indeterminate), False => (True => False, False => True, Indeterminate => Indeterminate), Indeterminate => (True => Indeterminate, False => Indeterminate, Indeterminate => Indeterminate)); Eq_Truth_Table2 : constant array (Triboolean, Boolean) of Triboolean := (True => (True => True, False => False), False => (True => False, False => True), Indeterminate => (True => Indeterminate, False => Indeterminate)); ------------------- -- To_TriBoolean -- ------------------- function To_TriBoolean (Value : Boolean) return Triboolean is begin if Value then return Triboolean'(True); else return Triboolean'(False); end if; end To_TriBoolean; ---------------- -- To_Boolean -- ---------------- function To_Boolean (Value : Triboolean) return Boolean is begin return Value = Triboolean'(True); end To_Boolean; ----------- -- "not" -- ----------- function "not" (Value : Triboolean) return Triboolean is begin case Value is when Triboolean'(True) => return Triboolean'(False); when Triboolean'(False) => return Triboolean'(True); when Triboolean'(Indeterminate) => return Triboolean'(Indeterminate); end case; end "not"; ----------- -- "and" -- ----------- function "and" (Value1, Value2 : Triboolean) return Triboolean is begin return And_Truth_Table1 (Value1, Value2); end "and"; function "and" (Value1 : Triboolean; Value2 : Boolean) return Triboolean is begin return And_Truth_Table2 (Value1, Value2); end "and"; function "and" (Value1 : Boolean; Value2 : Triboolean) return Triboolean is begin return And_Truth_Table2 (Value2, Value1); end "and"; ---------- -- "or" -- ---------- function "or" (Value1, Value2 : Triboolean) return Triboolean is begin return Or_Truth_Table1 (Value1, Value2); end "or"; function "or" (Value1 : Triboolean; Value2 : Boolean) return Triboolean is begin return Or_Truth_Table2 (Value1, Value2); end "or"; function "or" (Value1 : Boolean; Value2 : Triboolean) return Triboolean is begin return Or_Truth_Table2 (Value2, Value1); end "or"; ----------- -- "xor" -- ----------- function "xor" (Value1, Value2 : Triboolean) return Triboolean is begin return Xor_Truth_Table1 (Value1, Value2); end "xor"; function "xor" (Value1 : Triboolean; Value2 : Boolean) return Triboolean is begin return Xor_Truth_Table2 (Value1, Value2); end "xor"; function "xor" (Value1 : Boolean; Value2 : Triboolean) return Triboolean is begin return Xor_Truth_Table2 (Value2, Value1); end "xor"; --------- -- "=" -- --------- function "=" (Value1 : Boolean; Value2 : Triboolean) return Boolean is begin if Value1 then return Value2 = Triboolean'(True); else return Value2 = Triboolean'(False); end if; end "="; function "=" (Value1 : Triboolean; Value2 : Boolean) return Boolean is begin if Value2 then return Value1 = Triboolean'(True); else return Value1 = Triboolean'(False); end if; end "="; ----------- -- Equal -- ----------- function Equal (Value1 : Triboolean; Value2 : Boolean) return Triboolean is begin return Eq_Truth_Table2 (Value1, Value2); end Equal; ----------- -- Equal -- ----------- function Equal (Value1 : Boolean; Value2 : Triboolean) return Triboolean is begin return Eq_Truth_Table2 (Value2, Value1); end Equal; function Equal (Value1 : Triboolean; Value2 : Triboolean) return Triboolean is begin return Eq_Truth_Table1 (Value1, Value2); end Equal; ----------- -- Image -- ----------- function Image (Value : Triboolean) return String is begin case Value is when True => return "TRUE"; when False => return "FALSE"; when Indeterminate => return "INDETERMINATE"; end case; end Image; ----------- -- Value -- ----------- function Value (Str : String) return Triboolean is begin if Boolean'Value (Str) then return True; else return False; end if; exception when Constraint_Error => return Indeterminate; end Value; end GNATCOLL.Tribooleans; gnatcoll-core-21.0.0/src/gnatcoll-storage_pools-alignment.adb0000644000175000017500000001261213661715457024102 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2005-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with System.Storage_Pools; use System, System.Storage_Pools; with System.Storage_Elements; use System.Storage_Elements; with Ada.Unchecked_Deallocation; with Ada.Unchecked_Conversion; package body GNATCOLL.Storage_Pools.Alignment is type Storage_Element_Access is access Storage_Element; function Convert is new Ada.Unchecked_Conversion (System.Address, Storage_Element_Access); -------------- -- Allocate -- -------------- overriding procedure Allocate (Pool : in out Unbounded_No_Reclaim_Align_Pool; Address : out System.Address; Storage_Size : Storage_Count; Alignment : Storage_Count) is pragma Unreferenced (Alignment); -- We need to allocate more memory than actually requested, so that -- even if "new" returns an incorrect alignment, we have enough spare -- memory to return the correct alignment. We also always need a buffer -- of at least two Storage_Element to store the offset between the -- address from "new" and the one returned by the use, so that -- Deallocates works appropriately. -- Worst case is when "new" returned a correctly aligned chunk, and we -- then need to offset by Pool.Alignment bytes. Bytes_For_Offset : constant := 3; Align : constant Storage_Count := Pool.Alignment; Size : constant Storage_Offset := Storage_Size + Align + Bytes_For_Offset - 1; subtype Local_Storage_Array is Storage_Array (1 .. Size); type Ptr is access Local_Storage_Array; Allocated : constant Ptr := new Local_Storage_Array; Offset : constant Storage_Count := Align - Allocated.all'Address mod Align; begin Allocated (Offset - 2) := Storage_Element (Offset / 65_536); Allocated (Offset - 1) := Storage_Element ((Offset mod 65_536) / 256); Allocated (Offset) := Storage_Element (Offset mod 256); Address := Allocated.all'Address + Offset; end Allocate; ---------------- -- Deallocate -- ---------------- overriding procedure Deallocate (Pool : in out Unbounded_No_Reclaim_Align_Pool; Address : System.Address; Storage_Size : Storage_Count; Alignment : Storage_Count) is pragma Unreferenced (Alignment); Size : constant Storage_Offset := Storage_Size + Pool.Alignment; subtype Local_Storage_Array is Storage_Array (1 .. Size); type Ptr is access Local_Storage_Array; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Local_Storage_Array, Ptr); function Convert is new Ada.Unchecked_Conversion (System.Address, Ptr); Offset_High2 : constant Storage_Element := Convert (Address - 3).all; Offset_High : constant Storage_Element := Convert (Address - 2).all; Offset_Low : constant Storage_Element := Convert (Address - 1).all; Offset : constant Storage_Count := Storage_Count (Offset_High2) * 65_536 + Storage_Count (Offset_High) * 256 + Storage_Count (Offset_Low); Real_Address : constant System.Address := Address - Offset; Var : Ptr := Convert (Real_Address); begin Unchecked_Free (Var); end Deallocate; ------------------ -- Storage_Size -- ------------------ overriding function Storage_Size (Pool : Unbounded_No_Reclaim_Align_Pool) return Storage_Count is pragma Unreferenced (Pool); begin -- Intuitively, should return System.Memory_Size. But on Sun/Alsys, -- System.Memory_Size > System.Max_Int, which means all you can do with -- it is raise CONSTRAINT_ERROR... return Storage_Count'Last; end Storage_Size; end GNATCOLL.Storage_Pools.Alignment; gnatcoll-core-21.0.0/src/gnatcoll-formatters.ads0000644000175000017500000000771113661715457021461 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides routines to format the text representation of complex -- data structures. with GNATCOLL.Strings_Impl; package GNATCOLL.Formatters is generic with package Strings is new GNATCOLL.Strings_Impl.Strings (<>); procedure Columns_Vertical (Words : Strings.XString_Array; Width : Positive; Put_Line : not null access procedure (Line : Strings.XString); Pad : Strings.Char_Type := Strings.Space; Delimiter : Strings.Char_String := (1 => Strings.Space)); -- Procedure to format ordered phrases by columns vertically and output it -- to callback line by line. -- Number of columns limited by width and have to be calculated to minimize -- number of rows in each column. Output example: -- -- A_2.0161E-01 L_8.34E-02 W_3.4E-02 -- B_4.112135470E-01 M_9.83147859573364258000E-01 X_1.232E-01 -- C_1.8368E-01 N_4.7302106023E-01 Y_7.27181677268E-01 -- D_2.25542E-01 O_8.702573776245117190E-01 Z_1.415E-01 -- E_5.836335420609E-01 P_9.2786121368408203100E-01 -- F_7.226370573043823E-01 Q_9.2725968360900878900E-01 -- G_5.0E-03 R_7.7519929409027100E-01 -- H_6.59124374389648E-01 S_2.48095E-01 -- I_3.4402838E-01 T_9.0058088302612304700E-01 -- J_1.7E-02 U_6.0053098201752E-01 -- K_7.217630147933960E-01 V_8.02301645278930664E-01 -- -- Words is the array of strings to be formatted in the output. -- Width is the width limit of the output. -- Put_Line is the callback routine to take output line by line. -- Pad is the character filling the space after words to have same size in -- column for next column to be aligned. -- Delimiter is string delimiting the columns. generic with package Strings is new GNATCOLL.Strings_Impl.Strings (<>); End_Of_Line : Strings.Char_Type; function Columns_Vertical_XString (Words : Strings.XString_Array; Width : Positive; Pad : Strings.Char_Type := Strings.Space; Delimiter : Strings.Char_String := (1 => Strings.Space)) return Strings.XString; -- The same as above but returns formatted text at once end GNATCOLL.Formatters; gnatcoll-core-21.0.0/src/gnatcoll-plugins__windows.adb0000644000175000017500000000661413661715457022645 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with System; use System; with Interfaces.C; with Ada.Unchecked_Conversion; package body GNATCOLL.Plugins is type LPCSTR is access constant Interfaces.C.char; pragma Convention (C, LPCSTR); function LoadLibrary (lpLibFileName : LPCSTR) return Plugin; pragma Import (Stdcall, LoadLibrary, "LoadLibraryA"); procedure FreeLibrary (hModule : Plugin); pragma Import (Stdcall, FreeLibrary, "FreeLibrary"); function GetProcAddress (hModule : Plugin; lpProcName : LPCSTR) return Address; pragma Import (Stdcall, GetProcAddress, "GetProcAddress"); function GetLastError return Integer; pragma Import (Stdcall, GetLastError, "GetLastError"); function As_LPCSTR is new Ada.Unchecked_Conversion (Source => System.Address, Target => LPCSTR); ---------- -- Load -- ---------- function Load (Path : String) return Plugin is Local_Path : aliased constant String := Path & ASCII.NUL; begin return LoadLibrary (As_LPCSTR (Local_Path'Address)); end Load; --------------------- -- Routine_Address -- --------------------- function Routine_Address (P : Plugin; Name : String) return Address is RN : aliased constant String := Name & ASCII.NUL; begin return GetProcAddress (P, As_LPCSTR (RN'Address)); end Routine_Address; ------------------------ -- Last_Error_Message -- ------------------------ function Last_Error_Message return String is begin return "Last error code" & GetLastError'Img; end Last_Error_Message; ------------ -- Unload -- ------------ procedure Unload (P : in out Plugin) is begin FreeLibrary (P); P := No_Plugin; end Unload; end GNATCOLL.Plugins; gnatcoll-core-21.0.0/src/gnatcoll-traces.adb0000644000175000017500000021020513661715457020525 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2001-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ pragma Ada_2012; with Ada.Calendar; use Ada.Calendar; with Ada.Calendar.Formatting; use Ada.Calendar.Formatting; with Ada.Calendar.Time_Zones; use Ada.Calendar.Time_Zones; with Ada.Environment_Variables; with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Exceptions.Traceback; use Ada.Exceptions.Traceback; with Ada.IO_Exceptions; with Ada.Unchecked_Deallocation; with GNAT.Calendar.Time_IO; use GNAT.Calendar.Time_IO; with GNAT.OS_Lib; use GNAT.OS_Lib; with GNAT.Regpat; use GNAT.Regpat; with GNAT.Traceback; use GNAT.Traceback; with Interfaces.C_Streams; use Interfaces.C_Streams; with System.Address_Image; with System.Assertions; use System.Assertions; with GNATCOLL.Memory; with GNATCOLL.Mmap; use GNATCOLL.Mmap; with GNATCOLL.Templates; with GNATCOLL.Utils; use GNATCOLL.Utils; package body GNATCOLL.Traces is use type FILEs, size_t; use type GNATCOLL.Terminal.ANSI_Color, GNATCOLL.Terminal.ANSI_Style; use type GNATCOLL.Terminal.Full_Style; Max_Active_Decorators : constant := 40; -- Maximum number of active iterators On_Exception : On_Exception_Mode := Propagate; -- The behavior that should be adopted when something unexpected prevent -- the log stream to be written. -- Note: rev 1.5 of this file has a (disabled) support for symbolic -- tracebacks. -- ??? We could display the stack pointer with -- procedure Print_Sp is -- start : aliased Integer; -- begin -- Put_Line (System.Address_Image (Start'Address)); -- end; A_Zero : aliased constant String := "a" & ASCII.NUL; W_Zero : aliased constant String := "w" & ASCII.NUL; Reset_All : constant String := GNATCOLL.Terminal.Get_ANSI_Sequence ((Style => GNATCOLL.Terminal.Reset_All, Fg => GNATCOLL.Terminal.Unchanged, Bg => GNATCOLL.Terminal.Unchanged)); Default_Style : constant Message_Style := (Fg => GNATCOLL.Terminal.Reset, Bg => GNATCOLL.Terminal.Unchanged, Style => GNATCOLL.Terminal.Unchanged); -- The default style used for handles. This uses the terminal's -- default foreground. Default_Exception_Style : constant Message_Style := (Fg => GNATCOLL.Terminal.Black, Bg => GNATCOLL.Terminal.Red, Style => GNATCOLL.Terminal.Unchanged); -- Highlight with a red background. -- This is used to report unexpected exceptions when an exception -- occurrence is passed to Trace. type Decorator_Array is array (1 .. Max_Active_Decorators) of Trace_Decorator; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Trace_Handle_Record'Class, Trace_Handle); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Trace_Stream_Record'Class, Trace_Stream); type Stream_Factories; type Stream_Factories_List is access Stream_Factories; type Stream_Factories is record Name : GNAT.Strings.String_Access; Factory : Stream_Factory_Access; Next : Stream_Factories_List; end record; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Stream_Factories, Stream_Factories_List); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Stream_Factory'Class, Stream_Factory_Access); type Global_Vars is record Handles_List : Trace_Handle := null; -- The global list of all defined handles. Active_Decorators : Decorator_Array; Active_Last : Natural := 0; -- Never null after parsing the config file. -- Decorators on this list are all active, and also stored in the -- Handles_List. Wildcard_Handles_List : Trace_Handle := null; -- Contains the configuration for module names containing stars, for -- instance "*.EXCEPTIONS". Streams_List : Trace_Stream := null; -- The global list of all streams. -- The default stream is the first in the list. Factories_List : Stream_Factories_List := null; -- The global list of all factories. TZ : Time_Offset := UTC_Time_Offset; -- Time zone cache, assuming that the OS will not change time zones -- while this partition is running. Lock : aliased Atomic_Counter := 0; pragma Atomic (Lock); Absolute_Time : Trace_Decorator; Absolute_Date : Trace_Decorator; Micro_Time : Trace_Decorator; Colors : Trace_Decorator; Enclosing_Entity : Trace_Decorator; Location : Trace_Decorator; Finalize_Traces : Trace_Decorator; Split_Lines : Trace_Decorator; -- The predefined decorators. -- ??? These are also stored in the lists above, so we might not need -- them. Default_Activation : Boolean := False; -- Default activation status for debug handles (ie whether the -- configuration file contained "+"). -- ??? Could be handled via a "*" star handle Finalized : Boolean := False; -- Whether the package has been finalized. -- When this is true, some trace_handles will have been freed, and it -- is therefore illegal to access them. end record; Global : Global_Vars; type Elapse_Time_Trace is new Trace_Decorator_Record with null record; overriding procedure After_Message (Self : in out Elapse_Time_Trace; Handle : not null Logger; Msg : in out Msg_Strings.XString); type Stack_Trace is new Trace_Decorator_Record with null record; overriding procedure After_Message (Self : in out Stack_Trace; Handle : not null Logger; Msg : in out Msg_Strings.XString); type Count_Trace is new Trace_Decorator_Record with null record; overriding procedure Before_Message (Self : in out Count_Trace; Handle : not null Logger; Msg : in out Msg_Strings.XString); type Memory_Trace is new Trace_Decorator_Record with record Previous : GNATCOLL.Memory.Byte_Count := 0; end record; overriding procedure After_Message (Self : in out Memory_Trace; Handle : not null Logger; Msg : in out Msg_Strings.XString); type Ada_Memory_Trace is new Trace_Decorator_Record with record Previous : GNATCOLL.Memory.Byte_Count := 0; end record; overriding procedure After_Message (Self : in out Ada_Memory_Trace; Handle : not null Logger; Msg : in out Msg_Strings.XString); procedure Lock (The_Lock : aliased in out Atomic_Counter) with Inline_Always; procedure Unlock (The_Lock : aliased in out Atomic_Counter) with Inline_Always; -- For critical regions. A thread already owning the lock cannot try to -- take the lock again, or it will block. procedure Create_Exception_Handle (Handle : not null Trace_Handle); -- Create the exception handle associated with Handle. function Local_Sub_Second (T : Ada.Calendar.Time) return Integer; pragma Inline (Local_Sub_Second); -- Version of Local_Sub_Second taking advantage of the timezone cache -- return values in range 0 .. 999 function Find_Handle (Handle : Trace_Handle; Name_Upper_Case : String) return Trace_Handle; -- Return the debug handle associated with Unit_Name_Upper_Case, -- or null if there is none. The case of Unit_Name_Upper_Case is -- not changed. -- Note: this subprogram doesn't do any locking, it is the -- responsability of the called to make sure that not two tasks -- can access it at the same time. function Find_Wildcard_Handle (Unit_Name_Upper_Case : String) return Trace_Handle; -- Check whether there is a module name that contains a "*" and that can be -- used to provide the default configuration for Unit_Name_Upper_Case function Wildcard_Applies_To (Upper_Name : String; Upper_Star : String) return Boolean; -- Whether the module Upper_Name should take its default configuration from -- Upper_Wildcard_Name. function Find_Stream (Stream_Name : String; Relative_Path_To : Virtual_File; Append : Boolean) return Trace_Stream; -- Return the stream associated with that name (either an existing one or -- one created by a factory), or null if the default stream should be -- applied. -- The Stream_Name might include the settings for the stream, as in: -- "file.txt:buffer_size=0,async=true" procedure Put_Absolute_Time (Msg : in out Msg_Strings.XString); -- Print the absolute time in Handle. No locking is done, this is the -- responsability of the caller. No colors is modified either. function Config_File (Filename : Virtual_File; Default : Virtual_File) return Virtual_File; -- Return the name of the config file to use. -- If Filename is specified, this is the file to use, providing it exists. -- Otherwise, we use a .gnatdebug in the current directory, and if there is -- none, Default if it exists. -- The empty string is returned if no such file was found. procedure Register_Handle (Handle : not null Trace_Handle; Upper_Case : String; Finalize : Boolean := True); -- add Handle to the internal list and set default fields function Create_Internal (Unit_Name : String; Default : Default_Activation_Status := From_Config; Stream : Trace_Stream; Factory : Handle_Factory := null; Finalize : Boolean := True; Style : Message_Style; From_Config_File : Boolean) return Trace_Handle; -- Internal version of Create function Get_Process_Id return Integer; -- Return the process ID of the current process pragma Import (C, Get_Process_Id, "getpid"); type File_Stream_Record is new Trace_Stream_Record with record File : FILEs := NULL_Stream; Lock : aliased GNATCOLL.Atomic.Atomic_Counter := 0; Colors_Support : Boolean; end record; overriding procedure Put (Stream : in out File_Stream_Record; Str : Msg_Strings.XString); overriding procedure Close (Stream : in out File_Stream_Record); overriding function Supports_Color (Self : File_Stream_Record) return Boolean is (Self.Colors_Support); -- Logs to a file procedure Cache_Settings (Handle : not null Trace_Handle); -- Cache various settings in Handle, to avoid dispatching calls in Log -- and thus speed things up. -- These settings are changed much less frequently. ---------- -- Lock -- ---------- procedure Lock (The_Lock : aliased in out Atomic_Counter) is begin while True loop -- In this package, the lock is owned during the time it takes -- to Put a string to a stream (async streams go even faster). -- It doesn't seem worth adding a "delay" in this loop, though -- the standard implementation would be to have a delay on a -- random number, and increase the delay every time we have to -- loop until a given maximum. while The_Lock /= 0 loop null; end loop; exit when Sync_Add_And_Fetch (The_Lock'Unchecked_Access, 1) = 1; end loop; end Lock; ------------ -- Unlock -- ------------ procedure Unlock (The_Lock : aliased in out Atomic_Counter) is begin The_Lock := 0; end Unlock; ----------------- -- Find_Handle -- ----------------- function Find_Handle (Handle : Trace_Handle; Name_Upper_Case : String) return Trace_Handle is Tmp : Trace_Handle := Handle; begin while Tmp /= null and then Tmp.Name.all /= Name_Upper_Case loop Tmp := Tmp.Next; end loop; return Tmp; end Find_Handle; ------------------------- -- Wildcard_Applies_To -- ------------------------- function Wildcard_Applies_To (Upper_Name : String; Upper_Star : String) return Boolean is begin if Upper_Star (Upper_Star'First) = '*' then -- Test must include '.' in the suffix if Ends_With (Upper_Name, Upper_Star (Upper_Star'First + 1 .. Upper_Star'Last)) then return True; end if; elsif Upper_Star (Upper_Star'Last) = '*' then -- "MODULE.*" should include "MODULE" itself if Upper_Name = Upper_Star (Upper_Star'First .. Upper_Star'Last - 2) then return True; end if; -- Otherwise "MODULE.*" should match "MODULE.FOO" but not -- MODULEFOO.BAR if Starts_With (Upper_Name, Upper_Star (Upper_Star'First .. Upper_Star'Last - 1)) then return True; end if; end if; return False; end Wildcard_Applies_To; -------------------------- -- Find_Wildcard_Handle -- -------------------------- function Find_Wildcard_Handle (Unit_Name_Upper_Case : String) return Trace_Handle is Tmp : Trace_Handle := Global.Wildcard_Handles_List; begin while Tmp /= null loop if Wildcard_Applies_To (Upper_Name => Unit_Name_Upper_Case, Upper_Star => Tmp.Name.all) then return Tmp; end if; Tmp := Tmp.Next; end loop; return null; end Find_Wildcard_Handle; ------------------------ -- Show_Configuration -- ------------------------ procedure Show_Configuration (Output : Output_Proc) is Tmp : Trace_Handle := Global.Handles_List; function Stream_Name return String; -- Return the name of the stream if there is one function Stream_Name return String is begin if Tmp.Stream /= null and then Tmp.Stream /= Global.Streams_List then return " >" & Tmp.Stream.Name.all; else return ""; end if; end Stream_Name; begin if Global.Streams_List /= null then Output ("> " & Global.Streams_List.Name.all); end if; while Tmp /= null loop if Tmp.Active then Output (Tmp.Name.all & "=yes" & Stream_Name); elsif Tmp.all not in Trace_Decorator_Record'Class then -- Only output decorators when they are active Output (Tmp.Name.all & "=no" & Stream_Name); end if; Tmp := Tmp.Next; end loop; end Show_Configuration; ----------------- -- Find_Stream -- ----------------- function Find_Stream (Stream_Name : String; Relative_Path_To : Virtual_File; Append : Boolean) return Trace_Stream is procedure Add_To_Streams (Tmp : Trace_Stream); -------------------- -- Add_To_Streams -- -------------------- procedure Add_To_Streams (Tmp : Trace_Stream) is begin -- ??? Could use atomic operations to manipulate the -- list directly. Lock (Global.Lock); -- If possible, do not put this first on the list of streams, -- since it would become the default stream if Global.Streams_List = null then Global.Streams_List := Tmp; Tmp.Next := null; else Tmp.Next := Global.Streams_List.Next; Global.Streams_List.Next := Tmp; end if; Unlock (Global.Lock); end Add_To_Streams; Name : constant String := Trim (Stream_Name, Ada.Strings.Both); Tmp : Trace_Stream; Colon : Natural; TmpF : Stream_Factories_List; Default_Colors : GNATCOLL.Terminal.Supports_Color := GNATCOLL.Terminal.Auto; Term : GNATCOLL.Terminal.Terminal_Info; Supports_Buffer : Boolean := True; Buf_Size : size_t := 1; -- Line buffering by default begin if Name = "" then return null; end if; -- Do we have a matching existing stream? -- Since we use a linked list and never remove elements from -- the list, we do not need locking. Tmp := Global.Streams_List; while Tmp /= null loop if Tmp.Name.all = Name then return Tmp; end if; Tmp := Tmp.Next; end loop; -- Parse stream options Colon := Index (Name, ":"); if Colon < Name'First then Colon := Name'Last + 1; end if; declare Args : String_List_Access := Split (Name, ':'); begin for A of Args (Args'First + 1 .. Args'Last) loop if Starts_With (A.all, "buffer_size=") then begin Buf_Size := size_t'Value (A (A'First + 12 .. A'Last)); exception when Constraint_Error => -- Ignore not numeric buffer_size value and Buf_Size -- remains default. null; end; elsif Starts_With (A.all, "colors=") then declare V : constant String := To_Lower (A (A'First + 7 .. A'Last)); begin if V = "on" or else V = "true" then Default_Colors := GNATCOLL.Terminal.Yes; elsif V = "off" or else V = "false" then Default_Colors := GNATCOLL.Terminal.No; else Default_Colors := GNATCOLL.Terminal.Auto; end if; end; end if; end loop; Free (Args); end; -- Do we have a matching factory (if we start with "&")? if Name (Name'First .. Colon - 1) = "&1" then Term.Init_For_Stdout (Colors => Default_Colors); Tmp := new File_Stream_Record' (Name => new String'(Name), File => stdout, Colors_Support => Term.Has_ANSI_Colors, others => <>); Add_To_Streams (Tmp); Supports_Buffer := False; elsif Name (Name'First .. Colon - 1) = "&2" then Term.Init_For_Stderr (Colors => Default_Colors); Tmp := new File_Stream_Record' (Name => new String'(Name), File => stderr, Colors_Support => Term.Has_ANSI_Colors, others => <>); Add_To_Streams (Tmp); Supports_Buffer := False; elsif Name (Name'First) = '&' then Tmp := null; TmpF := Global.Factories_List; while TmpF /= null loop if TmpF.Name.all = Name (Name'First .. Colon - 1) then if Colon < Name'Last then Tmp := TmpF.Factory.New_Stream (Name (Colon + 1 .. Name'Last)); else Tmp := TmpF.Factory.New_Stream (""); end if; Tmp.Name := new String'(Name); Add_To_Streams (Tmp); exit; end if; TmpF := TmpF.Next; end loop; else declare use GNATCOLL.Templates; Now : constant Ada.Calendar.Time := Clock; Nam_Dollar : aliased String := "$"; Val_Dollar : aliased String := Trim (Get_Process_Id'Img, Ada.Strings.Both); Nam_D : aliased String := "D"; Val_D : aliased String := Image (Now, ISO_Date); Nam_T : aliased String := "T"; Val_T : aliased String := Val_D & Image (Now, "T%H%M%S"); Predef_Substitutions : constant Substitution_Array := ((Name => Nam_Dollar'Unchecked_Access, Value => Val_Dollar'Unchecked_Access), (Name => Nam_D'Unchecked_Access, Value => Val_D'Unchecked_Access), (Name => Nam_T'Unchecked_Access, Value => Val_T'Unchecked_Access)); function Substitute_Cb (Var : String; Quoted : Boolean) return String; -- Callback for variable substitution in Name -------------------- -- Substitute_Cb -- -------------------- function Substitute_Cb (Var : String; Quoted : Boolean) return String is pragma Unreferenced (Quoted); -- No way to "use Ada.Environment_Variables;" because of -- visibility conflict with Traces.Exists and GNAT can't -- discover it, see R924-001. begin if Ada.Environment_Variables.Exists (Var) then return Ada.Environment_Variables.Value (Var); else raise Invalid_Substitution; end if; end Substitute_Cb; N : constant String := Normalize_Pathname (Substitute (Str => Name (Name'First .. Colon - 1), Substrings => Predef_Substitutions, Callback => Substitute_Cb'Unrestricted_Access, Delimiter => '$'), +Relative_Path_To.Full_Name.all); N_Zero : aliased constant String := N & ASCII.NUL; F : FILEs; begin if Append then F := fopen (N_Zero'Address, mode => A_Zero'Address); else F := fopen (N_Zero'Address, mode => W_Zero'Address); end if; if F = NULL_Stream then F := stderr; end if; Term.Init_For_File (Colors => Default_Colors); Tmp := new File_Stream_Record' (Name => new String'(Name), File => F, Colors_Support => Term.Has_ANSI_Colors, others => <>); Add_To_Streams (Tmp); end; end if; if Tmp /= null and then Tmp.all in File_Stream_Record'Class and then Supports_Buffer then declare Dummy : int; begin Dummy := setvbuf (File_Stream_Record (Tmp.all).File, System.Null_Address, (case Buf_Size is when 0 => IONBF, -- unbuffered when 1 => IOLBF, -- line buffered when others => IOFBF), -- full buffered Buf_Size); end; end if; -- Else use the default stream return Tmp; end Find_Stream; ------------ -- Create -- ------------ function Create (Unit_Name : String; Default : Default_Activation_Status := From_Config; Stream : String := ""; Factory : Handle_Factory := null; Finalize : Boolean := True) return Trace_Handle is begin return Create_Internal (From_Config_File => False, Unit_Name => Unit_Name, Default => Default, Stream => Find_Stream (Stream, No_File, Append => False), Factory => Factory, Style => Default_Style, Finalize => Finalize); end Create; --------------------- -- Create_Internal -- --------------------- function Create_Internal (Unit_Name : String; Default : Default_Activation_Status := From_Config; Stream : Trace_Stream; Factory : Handle_Factory := null; Finalize : Boolean := True; Style : Message_Style; From_Config_File : Boolean) return Trace_Handle is Is_Star : constant Boolean := Starts_With (Unit_Name, "*.") or else Ends_With (Unit_Name, ".*"); Handle : Trace_Handle; Upper_Case : constant String := To_Upper (Unit_Name); Tmp2 : Trace_Handle; Wildcard : Trace_Handle; begin -- Do we already have an existing handle ? Handle := Find_Handle ((if Is_Star then Global.Wildcard_Handles_List else Global.Handles_List), Upper_Case); if Handle = null then if Factory /= null then Handle := Factory.all; end if; if Handle = null then Handle := new Trace_Handle_Record; end if; Register_Handle (Handle => Handle, Upper_Case => Upper_Case, Finalize => Finalize); -- Unless both settings are already known, check if we have a -- wildcard. if (Default = From_Config or else Stream = null) and then not Is_Star then Wildcard := Find_Wildcard_Handle (Handle.Name.all); if Wildcard /= null then Set_Active (Handle, Wildcard.Active); Handle.Forced_Active := True; -- Unless we specified an explicit stream, inherit it if Stream = null and then Wildcard.Stream /= null then Handle.Stream := Wildcard.Stream; Handle.Stream_Is_Default := Wildcard.Stream_Is_Default; end if; else Set_Active (Handle, Global.Default_Activation); end if; end if; end if; if Stream /= null then -- Only override when we are parsing the configuration file, so -- that if we have the following: -- Me : Trace_Handle := Create ("ME", Stream => "str1"); -- parse config file, which contains "ME=yes >str2" -- Me := Create ("ME", Stream => "str3") -- then "ME" is sent to "str2" (priority is given to the config -- file. if From_Config_File or else Handle.Stream_Is_Default then Handle.Stream := Stream; Handle.Stream_Is_Default := False; end if; -- A wildcard only impacts the stream of loggers if it has its own -- stream. elsif not Is_Star then -- Use the default stream. If we are still parsing the config -- file, we might not have this info yet, so we set Stream to -- 'null' and it will be overridden later if not From_Config_File and then Handle.Stream_Is_Default then Handle.Stream := Global.Streams_List; Handle.Stream_Is_Default := True; end if; end if; -- Set activation if not Handle.Forced_Active or else From_Config_File then case Default is when On => Handle.Forced_Active := True; Set_Active (Handle, Active => True); when Off => Handle.Forced_Active := True; Set_Active (Handle, Active => False); when From_Config => null; end case; end if; -- If we are declaring a "wildcard" handle, we need to check -- whether any existing handle would match (which will in -- general be the case, since handles are declared at -- elaboration time and star handles in the config file). if Is_Star then Tmp2 := Global.Handles_List; while Tmp2 /= null loop if Wildcard_Applies_To (Tmp2.Name.all, Upper_Star => Handle.Name.all) then -- Always override the status of matching streams: -- There are two scenarios here: -- - in a given config file, we always respect the order -- of declarations, thus wildcards should in general be put -- at the beginning. -- - if a wildcard is declared later on in Ada, we want -- it to impact existing streams as well (as a convenience -- for forcing specific settings from the code. -- -- So do not check Tmp2.Forced_Active Set_Active (Tmp2, Handle.Active); if Style /= Use_Default_Style and then Tmp2.Default_Style = Default_Style then Tmp2.Default_Style := Style; end if; if Tmp2.Stream_Is_Default and then Handle.Stream /= null then Tmp2.Stream := Handle.Stream; Tmp2.Stream_Is_Default := Handle.Stream_Is_Default; end if; end if; Tmp2 := Tmp2.Next; end loop; end if; if Style /= Use_Default_Style then Handle.Default_Style := Style; end if; Cache_Settings (Handle); return Handle; end Create_Internal; ------------ -- Exists -- ------------ function Exists (Unit_Name : String) return Boolean is Is_Star : constant Boolean := Starts_With (Unit_Name, "*.") or else Ends_With (Unit_Name, ".*"); Handle : Trace_Handle; Upper_Case : constant String := To_Upper (Unit_Name); begin Handle := Find_Handle ((if Is_Star then Global.Wildcard_Handles_List else Global.Handles_List), Upper_Case); return Handle /= null; end Exists; -------------------- -- Cache_Settings -- -------------------- procedure Cache_Settings (Handle : not null Trace_Handle) is begin -- If we have already registered the default decorators if Global.Colors /= null and then Handle.Stream /= null then Handle.With_Time := (Global.Absolute_Time.Active or else Global.Absolute_Date.Active) and then Handle.Stream.Supports_Time; Handle.With_Colors := Global.Colors.Active and then Handle.Stream.Supports_Color; end if; end Cache_Settings; --------------------- -- Register_Handle -- --------------------- procedure Register_Handle (Handle : not null Trace_Handle; Upper_Case : String; Finalize : Boolean := True) is Is_Star : constant Boolean := Starts_With (Upper_Case, "*.") or else Ends_With (Upper_Case, ".*"); begin Handle.Name := new String'(Upper_Case); Handle.Forced_Active := False; Handle.Count := 0; Handle.Timer := No_Time; Handle.Finalize := Finalize; Handle.Active := False; Handle.Stream_Is_Default := True; Lock (Global.Lock); if Is_Star then Handle.Next := Global.Wildcard_Handles_List; Global.Wildcard_Handles_List := Handle; else Handle.Next := Global.Handles_List; Global.Handles_List := Handle; end if; Unlock (Global.Lock); exception when others => Unlock (Global.Lock); raise; end Register_Handle; ---------------- -- Set_Active -- ---------------- procedure Set_Active (Handle : not null access Trace_Handle_Record'Class; Active : Boolean) is Tmp : Trace_Handle; Dec : Trace_Decorator; begin Handle.Active := Active; if Handle.all in Trace_Decorator_Record'Class then Dec := Trace_Decorator (Handle); if Dec /= Global.Colors and then Dec /= Global.Finalize_Traces and then Dec /= Global.Split_Lines then -- If active, store it in the list of active decorators if Active then for A in 1 .. Global.Active_Last loop if Global.Active_Decorators (A) = Dec then -- Already in the list return; end if; end loop; -- ??? Should check if we have too many decorators Global.Active_Last := Global.Active_Last + 1; Global.Active_Decorators (Global.Active_Last) := Dec; else for A in 1 .. Global.Active_Last loop if Global.Active_Decorators (A) = Dec then Global.Active_Decorators (A .. Global.Active_Last - 1) := Global.Active_Decorators (A + 1 .. Global.Active_Last); Global.Active_Last := Global.Active_Last - 1; exit; end if; end loop; end if; end if; if Dec = Global.Colors or else Dec = Global.Absolute_Time or else Dec = Global.Absolute_Date then Tmp := Global.Handles_List; while Tmp /= null loop Cache_Settings (Tmp); Tmp := Tmp.Next; end loop; end if; end if; end Set_Active; --------------- -- Is_Active -- --------------- function Is_Active (Handle : not null access Trace_Handle_Record'Class) return Boolean is begin return not Global.Finalized and then Handle.Active; end Is_Active; --------------- -- Unit_Name -- --------------- function Unit_Name (Handle : not null access Trace_Handle_Record'Class) return String is begin return Handle.Name.all; end Unit_Name; ----------------------------- -- Create_Exception_Handle -- ----------------------------- procedure Create_Exception_Handle (Handle : not null Trace_Handle) is S : Trace_Stream; begin if Handle.Exception_Handle = null then -- Unless the config file specified an explicit stream, -- we inherit the one from Handle. if Handle.Stream = Global.Streams_List then S := null; else S := Handle.Stream; end if; Handle.Exception_Handle := Create_Internal (Unit_Name => Handle.Name.all & ".EXCEPTIONS", From_Config_File => False, Stream => S, Default => (if Handle.Active then On else Off), Style => Default_Exception_Style); Cache_Settings (Handle.Exception_Handle); end if; end Create_Exception_Handle; ----------- -- Trace -- ----------- procedure Trace (Handle : not null access Trace_Handle_Record'Class; E : Ada.Exceptions.Exception_Occurrence; Msg : String := "Unexpected exception: "; Color : String) is begin if Debug_Mode then if Handle.With_Colors then Trace (Handle, E, Color & Msg); else Trace (Handle, E, Msg); end if; end if; end Trace; ----------- -- Trace -- ----------- procedure Trace (Handle : not null access Trace_Handle_Record'Class; E : Ada.Exceptions.Exception_Occurrence; Msg : String := "Unexpected exception: "; Style : Message_Style := Use_Default_Style) is begin if Debug_Mode and then not Global.Finalized -- module not terminated then Create_Exception_Handle (Trace_Handle (Handle)); Trace (Handle.Exception_Handle, Msg & Ada.Exceptions.Exception_Information (E), Style => Style); end if; end Trace; ----------- -- Trace -- ----------- procedure Trace (Handle : not null access Trace_Handle_Record'Class; Message : String; Color : String; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Debug_Mode then if Handle.With_Colors then Trace (Handle, Color & Message, Location => Location, Entity => Entity); else Trace (Handle, Message, Location => Location, Entity => Entity); end if; end if; end Trace; ----------- -- Trace -- ----------- procedure Trace (Handle : not null access Trace_Handle_Record'Class; Message : String; Style : Message_Style := Use_Default_Style; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is -- We want maximum performance for traces. This saves about 2% -- in single-threaded applications and sometimes 1% for multi-threaded -- apps. pragma Suppress (All_Checks); Merged_Style : Message_Style; begin -- Do not output anything until we have called Parse_Config_File, -- and do not output anything after we have called Finalized and -- potentially freed Handle. -- The stream is null if the trace was Create-d as On by default -- but Parse_Config_File was never called. if not Active (Handle) or else Handle.Stream = null then return; end if; declare Start, Last : Natural; Indent : constant Integer := Integer (Handle.Stream.Indentation); With_Color : constant Boolean := Handle.With_Colors; Msg : Msg_Strings.XString; begin for D in 1 .. Global.Active_Last loop Global.Active_Decorators (D).Start_Of_Line (Msg, Is_Continuation => False); end loop; if Indent > 0 then Msg.Append ((1 .. Indent * 3 => ' ')); end if; if With_Color then Merged_Style := Handle.Default_Style; if Style.Fg /= GNATCOLL.Terminal.Unchanged then Merged_Style.Fg := Style.Fg; end if; if Style.Bg /= GNATCOLL.Terminal.Unchanged then Merged_Style.Bg := Style.Bg; end if; if Style.Style /= GNATCOLL.Terminal.Unchanged then Merged_Style.Style := Style.Style; end if; Msg.Append (Cyan_Fg); end if; Msg.Append ('['); Msg.Append (Handle.Name.all); Msg.Append (']'); Msg.Append (' '); -- Decorate before the message for D in 1 .. Global.Active_Last loop Global.Active_Decorators (D).Before_Message (Trace_Handle (Handle), Msg); end loop; -- Add the message if Global.Split_Lines /= null and then Global.Split_Lines.Active then Start := Message'First; loop Last := Start; while Last <= Message'Last and then Message (Last) /= ASCII.LF loop Last := Last + 1; end loop; if With_Color then Msg.Append (GNATCOLL.Terminal.Get_ANSI_Sequence (Merged_Style)); end if; Msg.Append (Message (Start .. Last - 1)); Start := Last + 1; exit when Start > Message'Last; Msg.Append (ASCII.LF); for D in 1 .. Global.Active_Last loop Global.Active_Decorators (D).Start_Of_Line (Msg, Is_Continuation => True); end loop; if Indent > 0 then Msg.Append ((1 .. Indent * 3 => ' ')); end if; if With_Color then Msg.Append (Purple_Fg); end if; Msg.Append ('_'); Msg.Append (Handle.Name.all); Msg.Append ('_'); Msg.Append (' '); end loop; else if With_Color then Msg.Append (GNATCOLL.Terminal.Get_ANSI_Sequence (Merged_Style)); end if; Msg.Append (Message); end if; -- Decorate after the message if Global.Active_Last /= 0 then if With_Color then Msg.Append (Brown_Fg); end if; Msg.Append (' '); for D in 1 .. Global.Active_Last loop Global.Active_Decorators (D).After_Message (Trace_Handle (Handle), Msg); end loop; -- Remove trailing space if needed if Handle.With_Time then Put_Absolute_Time (Msg); end if; if Global.Location.Active then Msg.Append ("(loc: "); Msg.Append (Location); Msg.Append (')'); end if; if Global.Enclosing_Entity.Active then Msg.Append ("(entity:"); Msg.Append (Entity); Msg.Append (')'); end if; Msg.Trim (Ada.Strings.Right); end if; if With_Color then Msg.Append (Reset_All); end if; Msg.Append (ASCII.LF); Handle.Stream.Put (Msg); end; exception when others => case On_Exception is when Propagate => raise; when Ignore => null; when Deactivate => begin Close (Handle.Stream.all); exception when others => null; end; end case; end Trace; ------------ -- Assert -- ------------ procedure Assert (Handle : not null access Trace_Handle_Record'Class; Condition : Boolean; Error_Message : String; Message_If_Success : String := ""; Raise_Exception : Boolean := True; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Active (Handle) then if not Condition then Create_Exception_Handle (Trace_Handle (Handle)); Trace (Handle.Exception_Handle, Error_Message, Location => Location, Entity => Entity); if Raise_Exception then Raise_Assert_Failure (Error_Message & " (" & Entity & " at " & Location & ")"); end if; elsif Message_If_Success'Length /= 0 then Trace (Handle, Message_If_Success, Location, Entity); end if; end if; end Assert; --------------------- -- Increase_Indent -- --------------------- procedure Increase_Indent (Handle : access Trace_Handle_Record'Class := null; Msg : String := ""; Style : Message_Style := Use_Default_Style; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Handle /= null and then Handle.Stream /= null then if Msg /= "" then Trace (Handle, Msg, Style, Location => Location, Entity => Entity); end if; -- ??? Should we do this when the handle is inactive ? Increment (Handle.Stream.Indentation); end if; end Increase_Indent; --------------------- -- Decrease_Indent -- --------------------- procedure Decrease_Indent (Handle : access Trace_Handle_Record'Class := null; Msg : String := ""; Style : Message_Style := Use_Default_Style; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity) is begin if Handle /= null and then Handle.Stream /= null then -- The counter is a modulo type if Sync_Sub_And_Fetch (Handle.Stream.Indentation'Unchecked_Access, 1) = Minus_One then Handle.Stream.Indentation := 0; Trace (Handle, "Indentation error: too many decrease"); end if; if Msg /= "" then Trace (Handle, Msg, Style, Location => Location, Entity => Entity); end if; end if; end Decrease_Indent; ---------------------- -- Local_Sub_Second -- ---------------------- function Local_Sub_Second (T : Ada.Calendar.Time) return Integer is Y : Year_Number; M : Month_Number; D : Day_Number; H : Ada.Calendar.Formatting.Hour_Number; Mi : Ada.Calendar.Formatting.Minute_Number; S : Ada.Calendar.Formatting.Second_Number; Ss : Ada.Calendar.Formatting.Second_Duration; Ls : Boolean; begin Ada.Calendar.Formatting.Split (T, Y, M, D, H, Mi, S, Ss, Ls, Global.TZ); if Ss > 0.999 then return 999; else return Integer (Ss * 1000.0); end if; end Local_Sub_Second; ----------------------- -- Put_Absolute_Time -- ----------------------- procedure Put_Absolute_Time (Msg : in out Msg_Strings.XString) is T : constant Ada.Calendar.Time := Ada.Calendar.Clock; Z : String (1 .. 3) := "000"; Ms : constant String := Integer'Image (Local_Sub_Second (T)); begin Z (3 + 1 - (Ms'Length - 1) .. 3) := Ms (Ms'First + 1 .. Ms'Last); if Global.Absolute_Date.Active then if Global.Absolute_Time.Active then if Global.Micro_Time.Active then Msg.Append ("(" & Image (T, ISO_Date & " %T:%e") & ')'); else Msg.Append ("(" & Image (T, ISO_Date & " %T.") & Z & ')'); end if; else Msg.Append ("(" & Image (T, ISO_Date) & ')'); end if; else if Global.Micro_Time.Active then Msg.Append ("(" & Image (T, ISO_Date & " %T:%e") & ')'); else Msg.Append ("(" & Image (T, "%T.") & Z & ')'); end if; end if; end Put_Absolute_Time; -------------------- -- Before_Message -- -------------------- overriding procedure Before_Message (Self : in out Count_Trace; Handle : not null Trace_Handle; Msg : in out Msg_Strings.XString) is -- ??? Should we lock to get consistent counters ? Total : constant Atomic_Counter := Sync_Add_And_Fetch (Self.Count'Unchecked_Access, 1); Local : constant Atomic_Counter := Sync_Add_And_Fetch (Handle.Count'Unchecked_Access, 1); C : constant String := Atomic_Counter'Image (Total); H : constant String := Atomic_Counter'Image (Local); begin Msg.Append (H (H'First + 1 .. H'Last) & '/' & C (C'First + 1 .. C'Last) & ' '); end Before_Message; ------------------- -- After_Message -- ------------------- overriding procedure After_Message (Self : in out Memory_Trace; Handle : not null Trace_Handle; Msg : in out Msg_Strings.XString) is pragma Unreferenced (Handle); use GNATCOLL.Memory; Watermark : constant Watermark_Info := Get_Allocations; begin Msg.Append ("[Watermark:" & (if Watermark.Current > Self.Previous then '>' else '<') & Watermark.Current'Img & '/' & Watermark.High'Img & "]"); Self.Previous := Watermark.Current; end After_Message; ------------------- -- After_Message -- ------------------- overriding procedure After_Message (Self : in out Ada_Memory_Trace; Handle : not null Trace_Handle; Msg : in out Msg_Strings.XString) is pragma Unreferenced (Handle); use GNATCOLL.Memory; Watermark : constant Watermark_Info := Get_Ada_Allocations; begin if Watermark.High /= 0 then Msg.Append ("[AdaWatermark:" & (if Watermark.Current > Self.Previous then '>' else '<') & Watermark.Current'Img & '/' & Watermark.High'Img & "]"); end if; Self.Previous := Watermark.Current; end After_Message; ------------------- -- After_Message -- ------------------- overriding procedure After_Message (Self : in out Elapse_Time_Trace; Handle : not null Trace_Handle; Msg : in out Msg_Strings.XString) is pragma Unreferenced (Self); T : constant Ada.Calendar.Time := Ada.Calendar.Clock; Dur : Integer; begin if Handle.Timer /= No_Time then Dur := Integer ((T - Handle.Timer) * 1000); Msg.Append ("(elapsed:" & Integer'Image (Dur) & "ms)"); end if; Handle.Timer := T; end After_Message; ------------------- -- After_Message -- ------------------- overriding procedure After_Message (Self : in out Stack_Trace; Handle : not null Trace_Handle; Msg : in out Msg_Strings.XString) is pragma Unreferenced (Self, Handle); Tracebacks : GNAT.Traceback.Tracebacks_Array (1 .. 50); Len : Natural; begin Call_Chain (Tracebacks, Len); Msg.Append ("(callstack: "); for J in Tracebacks'First .. Len loop Msg.Append (System.Address_Image (Get_PC (Tracebacks (J))) & ' '); end loop; Msg.Append (")"); end After_Message; -------------------------- -- Add_Global_Decorator -- -------------------------- procedure Add_Global_Decorator (Decorator : not null Trace_Decorator; Name : String) is begin Register_Handle (Trace_Handle (Decorator), To_Upper (Name)); Decorator.Active := False; -- Set this flag, so that a "+" in the config file has no impact on -- decorators. Decorator.Forced_Active := True; end Add_Global_Decorator; ----------------- -- Config_File -- ----------------- function Config_File (Filename : Virtual_File; Default : Virtual_File) return Virtual_File is Env : GNAT.Strings.String_Access; Ret : Virtual_File; begin if Filename /= No_File and then Filename.Is_Regular_File then return Filename; end if; Env := Getenv (Config_File_Environment); -- First test the file described in the environment variable if Env /= null and then Env.all /= "" then Ret := Create (+Env.all); Free (Env); if Ret.Is_Regular_File then return Ret; end if; return No_File; end if; Free (Env); -- Then the file in the current directory Ret := Create_From_Dir (Get_Current_Dir, Default_Config_File); if Ret.Is_Regular_File then return Ret; end if; -- Then the file in the user's home directory Ret := Create_From_Dir (Get_Home_Directory, Default_Config_File); if Ret.Is_Regular_File then return Ret; end if; -- Finally the default file if Default /= No_File and then Is_Regular_File (Default) then return Default; end if; return No_File; end Config_File; ----------------------------- -- Register_Stream_Factory -- ----------------------------- procedure Register_Stream_Factory (Name : String; Factory : Stream_Factory_Access) is begin Lock (Global.Lock); Global.Factories_List := new Stream_Factories' (Name => new String'("&" & Name), Factory => Factory, Next => Global.Factories_List); Unlock (Global.Lock); end Register_Stream_Factory; ----------- -- Close -- ----------- procedure Close (Stream : in out Trace_Stream_Record) is begin Free (Stream.Name); end Close; --------- -- Put -- --------- overriding procedure Put (Stream : in out File_Stream_Record; Str : Msg_Strings.XString) is N : size_t; S : Msg_Strings.Char_Array; L : Natural; begin -- fwrite is thread safe on Windows and POSIX systems, -- we should not need locking. Str.Get_String (S, L); -- The call to fwrite is C, so will not raise exceptions Lock (Stream.Lock); N := fwrite (buffer => S.all'Address, size => size_t (L), count => 1, stream => Stream.File); Unlock (Stream.Lock); if N /= size_t (L) then -- ??? Could not write to file, disk full ? null; end if; end Put; ----------- -- Close -- ----------- overriding procedure Close (Stream : in out File_Stream_Record) is Status : int; pragma Unreferenced (Status); begin if Stream.File /= stdout and then Stream.File /= stderr then Status := fclose (Stream.File); Stream.File := NULL_Stream; end if; Close (Trace_Stream_Record (Stream)); end Close; ------------------ -- Parse_Config -- ------------------ procedure Parse_Config (Config : String; On_Exception : On_Exception_Mode := Propagate; Force_Activation : Boolean := True; Relative_Path_To : GNATCOLL.VFS.Virtual_File := GNATCOLL.VFS.Get_Current_Dir) is Handle : Trace_Handle; Dec : Trace_Decorator; Count : Natural := 0; procedure Create_Decorators; -- Create all default decorators, if not done yet function One_Line (Line : String) return Boolean; -- Callback function for each line of the configuration ----------------------- -- Create_Decorators -- ----------------------- procedure Create_Decorators is begin if Global.Colors = null then Set_Default_Stream ("&1"); Global.Micro_Time := new Trace_Decorator_Record; Add_Global_Decorator (Global.Micro_Time, "DEBUG.MICRO_TIME"); Dec := new Elapse_Time_Trace; Add_Global_Decorator (Dec, "DEBUG.ELAPSED_TIME"); Dec := new Stack_Trace; Add_Global_Decorator (Dec, "DEBUG.STACK_TRACE"); Dec := new Count_Trace; Add_Global_Decorator (Dec, "DEBUG.COUNT"); Dec := new Memory_Trace; Add_Global_Decorator (Dec, "DEBUG.MEMORY"); Dec := new Ada_Memory_Trace; Add_Global_Decorator (Dec, "DEBUG.ADA_MEMORY"); -- These are handled directly in Trace, but we should have them on -- the active list of decorators to know whether we need to add a -- space. Global.Absolute_Time := new Trace_Decorator_Record; Add_Global_Decorator (Global.Absolute_Time, "DEBUG.ABSOLUTE_TIME"); Global.Absolute_Date := new Trace_Decorator_Record; Add_Global_Decorator (Global.Absolute_Date, "DEBUG.ABSOLUTE_DATE"); Global.Enclosing_Entity := new Trace_Decorator_Record; Add_Global_Decorator (Global.Enclosing_Entity, "DEBUG.ENCLOSING_ENTITY"); Global.Location := new Trace_Decorator_Record; Add_Global_Decorator (Global.Location, "DEBUG.LOCATION"); -- The following are not decorators, and handled specially Global.Finalize_Traces := new Trace_Decorator_Record; Add_Global_Decorator (Global.Finalize_Traces, "DEBUG.FINALIZE_TRACES"); Global.Finalize_Traces.Active := True; Global.Split_Lines := new Trace_Decorator_Record; Add_Global_Decorator (Global.Split_Lines, "DEBUG.SPLIT_LINES"); Global.Split_Lines.Active := True; Global.Colors := new Trace_Decorator_Record; Add_Global_Decorator (Global.Colors, "DEBUG.COLORS"); end if; end Create_Decorators; S : constant String := "[ \t]*"; Line_Re : constant GNAT.Regpat.Pattern_Matcher := GNAT.Regpat.Compile ("^(?:" & "([^\s=:>+-]+)" & S -- 1 = name & "(?:=" & S & "(yes|no))?" & S -- 2 = active? & "(:[^\s>]+)?" & S -- 3 = options & "(?:>>?" & S & "(\S+))?" & S -- 4 = stream & "|" & "(>>?\S+)?" & S -- 5 = default stream & "|" & "(\+)" & S -- 6 = "+" & ")?" -- line can be empty & S & "(?:(?:#|--).*)?" -- end of line comments & "\r?$"); function One_Line (Line : String) return Boolean is M : GNAT.Regpat.Match_Array (0 .. 6); Group_Name : constant := 1; Group_Active : constant := 2; Group_Options : constant := 3; Group_Stream : constant := 4; Group_Default_Stream : constant := 5; Group_All : constant := 6; begin Count := Count + 1; if Line = "" then return True; end if; Match (Line_Re, Line, Matches => M); if M (0) = No_Match then if On_Exception = Propagate then raise Constraint_Error with "Line " & Count'Img & ": """ & Line & """ is not recognised."; end if; elsif M (Group_All) /= No_Match then Global.Default_Activation := True; Handle := Global.Handles_List; while Handle /= null loop if not Handle.Forced_Active then Set_Active (Handle, True); -- A later declaration of the stream in the code -- should not be allowed to reset Active to False Handle.Forced_Active := True; end if; Handle := Handle.Next; end loop; elsif M (Group_Default_Stream) /= No_Match then Set_Default_Stream (Config (M (Group_Default_Stream).First .. M (Group_Default_Stream).Last), Config_File => Relative_Path_To); elsif M (Group_Name) /= No_Match then declare Active : constant Default_Activation_Status := (if M (Group_Active) = No_Match or else Config (M (Group_Active).First .. M (Group_Active).Last) /= "no" then On else Off); Stream : Trace_Stream := null; Style : Message_Style := Default_Style; begin -- Do we have options for this handle ? if M (Group_Options) /= No_Match then declare use GNATCOLL.Terminal; Options : String_List_Access := Split (Config (M (Group_Options).First .. M (Group_Options).Last), ':'); begin for Opt of Options.all loop if Starts_With (Opt.all, "fg=") then Style.Fg := ANSI_Color'Value (Opt (Opt'First + 3 .. Opt'Last)); elsif Starts_With (Opt.all, "bg=") then Style.Bg := ANSI_Color'Value (Opt (Opt'First + 3 .. Opt'Last)); elsif Starts_With (Opt.all, "style=") then Style.Style := ANSI_Style'Value (Opt (Opt'First + 6 .. Opt'Last)); end if; end loop; Free (Options); end; end if; -- What stream is this sent to ? if M (Group_Stream) /= No_Match then declare Save : Integer := M (Group_Stream).First; Append : Boolean := False; begin if Save + 1 <= M (Group_Stream).Last and then Config (Save) = '>' then Append := True; Save := Save + 1; end if; Stream := Find_Stream (Config (Save .. M (Group_Stream).Last), Relative_Path_To, Append); end; end if; Handle := Create_Internal (Config (M (Group_Name).First .. M (Group_Name).Last), From_Config_File => True, Default => Active, Style => Style, Stream => Stream); end; end if; return True; end One_Line; begin if not Debug_Mode then return; end if; GNATCOLL.Traces.On_Exception := On_Exception; if Force_Activation or else Config /= "" then Create_Decorators; end if; if Config /= "" then Split (Config, (1 => ASCII.LF), One_Line'Access); end if; end Parse_Config; ----------------------- -- Parse_Config_File -- ----------------------- procedure Parse_Config_File (Filename : Virtual_File; Default : Virtual_File := No_File; On_Exception : On_Exception_Mode := Propagate; Force_Activation : Boolean := True) is File_Name : constant Virtual_File := Config_File (Filename, Default); Buffer : Str_Access; File : Mapped_File; begin if not Debug_Mode then return; end if; GNATCOLL.Traces.On_Exception := On_Exception; if File_Name = No_File then Parse_Config ("", On_Exception, Force_Activation => Force_Activation); else begin File := Open_Read (+File_Name.Full_Name); exception when Ada.IO_Exceptions.Name_Error => Parse_Config ("", On_Exception, Force_Activation => Force_Activation); return; end; Read (File); Buffer := Data (File); Parse_Config (Config => String (Buffer (1 .. Last (File))), On_Exception => On_Exception, Force_Activation => Force_Activation, Relative_Path_To => File_Name.Dir); Close (File); end if; end Parse_Config_File; ----------------------- -- Parse_Config_File -- ----------------------- procedure Parse_Config_File (Filename : String := ""; Default : String := ""; On_Exception : On_Exception_Mode := Propagate; Force_Activation : Boolean := True) is F_Filename : Virtual_File; F_Default : Virtual_File; begin if Filename = "" then F_Filename := No_File; else F_Filename := Create_From_Base (+Filename); end if; if Default = "" then F_Default := No_File; else F_Default := Create_From_Base (+Default); end if; Parse_Config_File (F_Filename, F_Default, On_Exception, Force_Activation); end Parse_Config_File; -------------- -- Finalize -- -------------- procedure Finalize is Tmp : Trace_Handle; Next : Trace_Handle; TmpS : Trace_Stream; NextS : Trace_Stream; TmpF : Stream_Factories_List; NextF : Stream_Factories_List; begin if not Global.Finalized -- Might never have been initialized at all and then Global.Finalize_Traces /= null and then Global.Finalize_Traces.Active then Lock (Global.Lock); Tmp := Global.Handles_List; while Tmp /= null loop Next := Tmp.Next; if Tmp.Finalize then Free (Tmp.Name); Unchecked_Free (Tmp); end if; Tmp := Next; end loop; Global.Handles_List := null; Tmp := Global.Wildcard_Handles_List; while Tmp /= null loop Next := Tmp.Next; if Tmp.Finalize then Free (Tmp.Name); Unchecked_Free (Tmp); end if; Tmp := Next; end loop; Global.Wildcard_Handles_List := null; TmpS := Global.Streams_List; while TmpS /= null loop NextS := TmpS.Next; Close (TmpS.all); Unchecked_Free (TmpS); TmpS := NextS; end loop; Global.Streams_List := null; TmpF := Global.Factories_List; while TmpF /= null loop NextF := TmpF.Next; Free (TmpF.Name); Unchecked_Free (TmpF.Factory); Unchecked_Free (TmpF); TmpF := NextF; end loop; Global.Factories_List := null; Unlock (Global.Lock); end if; Global.Finalized := True; end Finalize; --------------------- -- For_Each_Handle -- --------------------- procedure For_Each_Handle (Proc : not null Handlers_Proc) is Tmp : Trace_Handle := Global.Handles_List; begin while Tmp /= null loop Proc (Tmp); Tmp := Tmp.Next; end loop; end For_Each_Handle; ------------------------ -- Set_Default_Stream -- ------------------------ procedure Set_Default_Stream (Name : String; Config_File : Virtual_File := No_File) is S : Trace_Stream; T : Trace_Stream; H : Trace_Handle; begin if Name'Length > 2 and then Name (Name'First .. Name'First + 1) = ">>" then S := Find_Stream (Name (Name'First + 2 .. Name'Last), Config_File.Dir, Append => True); elsif Name (Name'First) = '>' then S := Find_Stream (Name (Name'First + 1 .. Name'Last), Config_File.Dir, Append => False); else S := Find_Stream (Name, Config_File.Dir, Append => False); end if; if S /= null then -- Put it first in the list Lock (Global.Lock); if Global.Streams_List /= S then T := Global.Streams_List; while T.Next /= S loop T := T.Next; end loop; T.Next := S.Next; S.Next := Global.Streams_List; Global.Streams_List := S; end if; -- Apply the default stream for all streams that do not have an -- explicit one H := Global.Handles_List; while H /= null loop if H.Stream = null or else H.Stream_Is_Default then H.Stream := S; H.Stream_Is_Default := True; Cache_Settings (H); end if; H := H.Next; end loop; Unlock (Global.Lock); end if; end Set_Default_Stream; ----------- -- Count -- ----------- function Count (Handler : not null access Trace_Handle_Record'Class) return Natural is begin return Natural (Handler.Count); end Count; ------------ -- Create -- ------------ function Create (Handle : Trace_Handle; Message : String := ""; Location : String := GNAT.Source_Info.Source_Location; Entity : String := GNAT.Source_Info.Enclosing_Entity; Style : Message_Style := Default_Block_Style) return Block_Trace_Handle is begin return Result : Block_Trace_Handle do if Active (Handle) then Result.Me := Handle; Result.Style := Style; Result.Loc := new String'(Entity & ':' & Location); if Message /= "" then Increase_Indent (Handle, "Entering " & Result.Loc.all & ' ' & Message, Style => Style, Location => "", Entity => ""); else Increase_Indent (Handle, "Entering " & Result.Loc.all, Style => Style, Location => "", Entity => ""); end if; end if; end return; end Create; -------------- -- Finalize -- -------------- overriding procedure Finalize (Self : in out Block_Trace_Handle) is begin -- If we were active when Create was called if Self.Me /= null then Decrease_Indent (Self.Me, "Leaving " & Self.Loc.all, Style => Self.Style, Location => "", -- avoid duplicate info in the output Entity => ""); end if; Free (Self.Loc); end Finalize; end GNATCOLL.Traces; gnatcoll-core-21.0.0/src/gnatcoll-promises.adb0000644000175000017500000003136613661715457021116 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Containers.Vectors; with Ada.Exceptions; use Ada.Exceptions; with Ada.Unchecked_Deallocation; with GNAT.Strings; use GNAT.Strings; with GNATCOLL.Atomic; use GNATCOLL.Atomic; package body GNATCOLL.Promises is use type Impl.Promise_Callback_Access; package Cb_Vectors is new Ada.Containers.Vectors (Positive, Impl.Promise_Callback_Access, Impl."="); ---------- -- Free -- ---------- procedure Free (Self : in out Freeable_Access) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (IFreeable'Class, Freeable_Access); begin if Self /= null then Free (Self.all); Unchecked_Free (Self); end if; end Free; --------------- -- Subscribe -- --------------- procedure Subscribe (Self : Promise_Chain) is begin null; end Subscribe; ---------- -- Impl -- ---------- package body Impl is ------------------- -- Dispatch_Free -- ------------------- procedure Dispatch_Free (Self : in out IPromise_Data'Class) is begin Free (Self); end Dispatch_Free; end Impl; -------------- -- Promises -- -------------- package body Promises is type T_Access is access all T; type Promise_Data is new Impl.IPromise_Data with record State : aliased Promise_State := Pending; Callbacks : Cb_Vectors.Vector; -- Need a vector here, but should try to limit memory allocs. -- A bounded vector might be more efficient, and sufficient in -- practice. Value : T_Access; -- ??? Using the ada-traits-containers approach, we could avoid -- some memory allocation here. Reason : GNAT.Strings.String_Access; end record; type Promise_Data_Access is access all Promise_Data'Class; overriding procedure Free (Self : in out Promise_Data); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (T, T_Access); --------------- -- Get_State -- --------------- function Get_State (Self : Promise'Class) return Actual_Promise_State is D : constant not null access Promise_Data'Class := Promise_Data_Access (Impl.Promise_Pointers.Unchecked_Get (Self)); begin return Actual_Promise_State (D.State); end Get_State; ------------ -- Create -- ------------ function Create return Promise is begin return P : Promise do P.Set (Data => Promise_Data' (Callbacks => Cb_Vectors.Empty_Vector, State => Pending, Value => null, Reason => null)); end return; end Create; ---------- -- Free -- ---------- overriding procedure Free (Self : in out Promise_Data) is begin Unchecked_Free (Self.Value); Free (Self.Reason); end Free; --------------- -- Set_Value -- --------------- procedure Set_Value (Self : in out Promise; R : T) is D : constant not null access Promise_Data'Class := Promise_Data_Access (Impl.Promise_Pointers.Unchecked_Get (Self)); Old : Actual_Promise_State; begin loop Old := Actual_Promise_State (Sync_Val_Compare_And_Swap_Counter (Ptr => D.State'Access, Oldval => Pending, Newval => Resolving)); case Old is when Resolved | Failed => -- Promise has already been completed, this is an error return; when Resolving | Failing | Subscribing => -- Try again null; when Pending => -- OK, we can change the state for Cb of D.Callbacks loop Callback_Access (Cb).On_Next (R); Free (Freeable_Access (Cb)); end loop; D.Callbacks.Clear; -- No longer needed, release them D.Value := new T'(R); D.State := Resolved; -- Fully resolved now exit; end case; end loop; end Set_Value; --------------- -- Set_Error -- --------------- procedure Set_Error (Self : in out Promise; Reason : String) is D : constant not null access Promise_Data'Class := Promise_Data_Access (Impl.Promise_Pointers.Unchecked_Get (Self)); Old : Actual_Promise_State; begin loop Old := Actual_Promise_State (Sync_Val_Compare_And_Swap_Counter (Ptr => D.State'Access, Oldval => Pending, Newval => Failing)); case Old is when Resolved | Failed => -- Promise has already been completed, this is an error return; when Resolving | Failing | Subscribing => -- Try again null; when Pending => -- OK, we can change the state for Cb of D.Callbacks loop Callback_Access (Cb).On_Error (Reason); Free (Freeable_Access (Cb)); end loop; D.Callbacks.Clear; -- No longer needed, release them D.Reason := new String'(Reason); D.State := Failed; -- Fully failed now exit; end case; end loop; end Set_Error; --------------- -- Subscribe -- --------------- procedure Subscribe (Self : Promise; Cb : not null access Callback'Class) is D : constant not null access Promise_Data'Class := Promise_Data_Access (Impl.Promise_Pointers.Unchecked_Get (Self)); -- ??? Unrestricted_Access is temporary, so that user can -- use "new Cb" directly in the call to Subscribe. C : Callback_Access := Cb.all'Unrestricted_Access; Old : Actual_Promise_State; begin loop Old := Actual_Promise_State (Sync_Val_Compare_And_Swap_Counter (Ptr => D.State'Access, Oldval => Pending, Newval => Subscribing)); case Old is when Resolving | Failing | Subscribing => -- Try again null; when Resolved => -- We don't need to change D, so we leave the state to -- Pending C.On_Next (D.Value.all); Free (Freeable_Access (C)); return; when Failed => C.On_Error (D.Reason.all); Free (Freeable_Access (C)); return; when Pending => D.Callbacks.Append (Impl.Promise_Callback_Access (C)); D.State := Pending; exit; end case; end loop; end Subscribe; ----------- -- "and" -- ----------- function "and" (Self : Promise; Cb : Callback_List) return Promise_Chain is begin for C of Cb loop Self.Subscribe (C); end loop; return Promise_Chain'(null record); end "and"; ----------- -- "and" -- ----------- function "and" (Self : Promise; Cb : not null access Callback'Class) return Promise_Chain is begin Self.Subscribe (Cb); return Promise_Chain'(null record); end "and"; --------- -- "&" -- --------- function "&" (Cb : not null access Callback'Class; Cb2 : not null access Callback'Class) return Callback_List is begin return (Cb.all'Unrestricted_Access, Cb2.all'Unrestricted_Access); end "&"; --------- -- "&" -- --------- function "&" (List : Callback_List; Cb2 : not null access Callback'Class) return Callback_List is begin return List & (1 => Cb2.all'Unrestricted_Access); end "&"; end Promises; ------------ -- Chains -- ------------ package body Chains is ----------- -- "and" -- ----------- function "and" (Input : Input_Promises.Promise; Cb : not null access Callback'Class) return Output_Promises.Promise is begin Cb.Promise := Output_Promises.Create; Input_Promises.Subscribe (Input, Cb.all'Unrestricted_Access); return Cb.Promise; end "and"; ----------- -- "and" -- ----------- function "and" (Input : Input_Promises.Promise; Cb : Callback_List) return Output_Promises.Promise is P : constant Output_Promises.Promise := Input and Cb.Cb; begin for C of Cb.Cb2 loop Input_Promises.Subscribe (Input, C); end loop; return P; end "and"; ------------------- -- Is_Registered -- ------------------- function Is_Registered (Self : not null access Callback'Class) return Boolean is begin return Self.Promise.Is_Created; end Is_Registered; ------------------- -- Is_Registered -- ------------------- function Is_Registered (Self : Callback_List) return Boolean is begin return Self.Cb.Promise.Is_Created; end Is_Registered; ------------- -- On_Next -- ------------- overriding procedure On_Next (Self : in out Callback; P : Input_Promises.Result_Type) is begin On_Next (Callback'Class (Self), P, Self.Promise); exception when E : others => Self.Promise.Set_Error (Exception_Message (E)); end On_Next; -------------- -- On_Error -- -------------- overriding procedure On_Error (Self : in out Callback; Reason : String) is begin -- Propagate the failure Self.Promise.Set_Error (Reason); end On_Error; ----------- -- "&" -- ----------- function "&" (Cb : not null access Callback'Class; Cb2 : not null access Input_Promises.Callback'Class) return Callback_List is begin return Callback_List' (N => 1, Cb => Cb.all'Unrestricted_Access, Cb2 => (1 => Cb2.all'Unrestricted_Access)); end "&"; ----------- -- "&" -- ----------- function "&" (List : Callback_List; Cb2 : not null access Input_Promises.Callback'Class) return Callback_List is begin return Callback_List' (N => List.N + 1, Cb => List.Cb, Cb2 => List.Cb2 & (1 => Cb2.all'Unrestricted_Access)); end "&"; end Chains; end GNATCOLL.Promises; gnatcoll-core-21.0.0/src/gnatcoll-json-utility.adb0000644000175000017500000002132713743647711021721 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2011-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ pragma Ada_2012; with Ada.Characters.Wide_Wide_Latin_1; use Ada.Characters.Wide_Wide_Latin_1; with Interfaces; use Interfaces; with GNAT.Encode_UTF8_String; with GNAT.Decode_UTF8_String; with GNATCOLL.Strings; package body GNATCOLL.JSON.Utility is use Ada.Strings.Unbounded; To_Hex : constant array (Unsigned_16 range 0 .. 15) of Character := "0123456789ABCDEF"; -------------------------------- -- Escape_Non_Print_Character -- -------------------------------- function Escape_Non_Print_Character (C : Wide_Wide_Character) return String is Code : constant Unsigned_32 := Wide_Wide_Character'Pos (C); Buf : String (1 .. 12); Last : Natural := Buf'First - 1; procedure Append_Escaped (Code : Unsigned_16); -------------------- -- Append_Escaped -- -------------------- procedure Append_Escaped (Code : Unsigned_16) is begin Last := Last + 6; Buf (Last - 5 .. Last - 4) := "\u"; Buf (Last - 3) := To_Hex ((Code / 16#1000#) mod 16#10#); Buf (Last - 2) := To_Hex ((Code / 16#100#) mod 16#10#); Buf (Last - 1) := To_Hex ((Code / 16#10#) mod 16#10#); Buf (Last) := To_Hex (Code mod 16#10#); end Append_Escaped; begin if Code <= 16#FFFF# then Append_Escaped (Unsigned_16 (Code)); else -- Represent character as surrogate pair Append_Escaped (16#D800# + Unsigned_16 ((Code - 16#1_0000#) / 16#400#)); Append_Escaped (16#DC00# + Unsigned_16 (Code mod 16#400#)); end if; return Buf (Buf'First .. Last); end Escape_Non_Print_Character; ------------------- -- Escape_String -- ------------------- function Escape_String (Text : UTF8_XString) return Unbounded_String is Str : GNATCOLL.Strings.Char_Array; Text_Length : Natural; Ret : Unbounded_String; Low : Natural; W_Chr : Wide_Wide_Character; begin Text.Get_String (Str, Text_Length); Append (Ret, '"'); Low := 1; while Low <= Text_Length loop -- UTF-8 sequence is maximum 4 characters long according to RFC3629 begin GNAT.Decode_UTF8_String.Decode_Wide_Wide_Character (String (Str (Low .. Natural'Min (Text_Length, Low + 3))), Low, W_Chr); exception when Constraint_Error => -- Skip the character even if it is invalid. Low := Low + 1; W_Chr := NUL; end; case W_Chr is when NUL => Append (Ret, "\u0000"); when '"' => Append (Ret, "\"""); when '\' => Append (Ret, "\\"); when BS => Append (Ret, "\b"); when FF => Append (Ret, "\f"); when LF => Append (Ret, "\n"); when CR => Append (Ret, "\r"); when HT => Append (Ret, "\t"); when others => if Wide_Wide_Character'Pos (W_Chr) < 32 then Append (Ret, Escape_Non_Print_Character (W_Chr)); elsif Wide_Wide_Character'Pos (W_Chr) >= 16#80# then Append (Ret, Escape_Non_Print_Character (W_Chr)); else Append (Ret, "" & Character'Val (Wide_Wide_Character'Pos (W_Chr))); end if; end case; end loop; Append (Ret, '"'); return Ret; end Escape_String; ---------------------- -- Un_Escape_String -- ---------------------- function Un_Escape_String (Text : String; Low : Natural; High : Natural) return UTF8_XString is First : Integer; Last : Integer; Unb : UTF8_XString; Idx : Natural; begin First := Low; Last := High; -- Trim blanks and double quotes while First <= High and then Text (First) = ' ' loop First := First + 1; end loop; if First <= High and then Text (First) = '"' then First := First + 1; end if; while Last >= Low and then Text (Last) = ' ' loop Last := Last - 1; end loop; if Last >= Low and then Text (Last) = '"' then Last := Last - 1; end if; Idx := First; while Idx <= Last loop if Text (Idx) = '\' then Idx := Idx + 1; if Idx > High then raise Invalid_JSON_Stream with "Unexpected escape character at end of line"; end if; -- See http://tools.ietf.org/html/rfc4627 for the list of -- characters that can be escaped. case Text (Idx) is when 'u' | 'U' => declare Lead : constant Unsigned_16 := Unsigned_16'Value ("16#" & Text (Idx + 1 .. Idx + 4) & "#"); Trail : Unsigned_16; Char : Wide_Wide_Character; begin Char := Wide_Wide_Character'Val (Lead); -- If character is high surrogate and next character is -- low surrogate then them represent one non-BMP -- character. if Lead in 16#D800# .. 16#DBFF# and then Text (Idx + 5) = '\' and then Text (Idx + 6) in 'u' | 'U' then Trail := Unsigned_16'Value ("16#" & Text (Idx + 7 .. Idx + 10) & '#'); Char := Wide_Wide_Character'Val (16#1_0000# + Unsigned_32 (Lead and 16#03FF#) * 16#0400# + Unsigned_32 (Trail and 16#03FF#)); Idx := Idx + 6; end if; Unb.Append (GNAT.Encode_UTF8_String.Encode_Wide_Wide_String ((1 => Char))); Idx := Idx + 4; end; when '"' => Unb.Append ('"'); when '/' => Unb.Append ('/'); when '\' => Unb.Append ('\'); when 'b' => Unb.Append (ASCII.BS); when 'f' => Unb.Append (ASCII.FF); when 'n' => Unb.Append (ASCII.LF); when 'r' => Unb.Append (ASCII.CR); when 't' => Unb.Append (ASCII.HT); when others => raise Invalid_JSON_Stream with "Unexpected escape sequence '\" & Text (Idx) & "'"; end case; else Unb.Append (Text (Idx)); end if; Idx := Idx + 1; end loop; return Unb; end Un_Escape_String; end GNATCOLL.JSON.Utility; gnatcoll-core-21.0.0/src/gnatcoll-refcount.adb0000644000175000017500000003526113661715457021100 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- Notes on the implementation of weak pointers: -- There are several ways in which a weak pointer can be implemented: -- - Using two counters (one for full references, one for weak). When both -- reach 0, the memory blocks is freed; when only the first reaches 0, -- the element is released, and the block can be resized. -- This is hard to make task safe without using critical section though. -- - store a doubly-linked list of weak pointers along with the counter. -- When the counter reaches 0, change each of the weak pointers to null. -- This requires more memory. -- - (our choice) make the weak pointer a smart pointer pointing to the -- same data: -- smart_ptr ---> chunk1: counter + element + pointer to chunk2 -- weak_ptr ---> chunk2: weak_counter + pointer to chunk1 pragma Ada_2012; with Ada.Finalization; use Ada.Finalization; with Ada.Unchecked_Conversion; with Ada.Unchecked_Deallocation; with GNATCOLL.Atomic; use GNATCOLL.Atomic; with System; use System; package body GNATCOLL.Refcount is function Inc_Ref (R : access Counters; Atomic : Boolean) return Atomic_Counter with Inline; -- Increase the refcount and return the new value function Inc_Ref (R : access Counters; Atomic : Boolean) return Boolean; -- Increase the refcount only if it was non-zero, returns True if the -- increment has occurred. procedure Inc_Ref (R : access Weak_Data; Atomic : Boolean) with Inline; -- Increase the refcount procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Weak_Data, Weak_Data_Access); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Refcounted'Class, Refcounted_Access); procedure Finalize (Data : in out Weak_Data_Access; Atomic : Boolean); -- Decrease refcount and free memory if needed function Sync_Bool_Compare_And_Swap is new Atomic.Sync_Bool_Compare_And_Swap (Weak_Data, Weak_Data_Access); ------------- -- Inc_Ref -- ------------- function Inc_Ref (R : access Counters; Atomic : Boolean) return Boolean is Tmp : Atomic_Counter; Tm2 : Atomic_Counter; begin if Atomic then Tmp := R.Refcount; if Tmp = 0 then return False; end if; loop Tm2 := Sync_Val_Compare_And_Swap_Counter (R.Refcount'Access, Tmp, Atomic_Counter'Succ (Tmp)); if Tm2 = Tmp then return True; elsif Tm2 = 0 then return False; else Tmp := Tm2; end if; end loop; else if R.Refcount = 0 then return False; end if; R.Refcount := Atomic_Counter'Succ (R.Refcount); return True; end if; end Inc_Ref; function Inc_Ref (R : access Counters; Atomic : Boolean) return Atomic_Counter is begin if Atomic then return Sync_Add_And_Fetch (R.Refcount'Access, 1); else Unsafe_Increment (R.Refcount); return R.Refcount; end if; end Inc_Ref; procedure Inc_Ref (R : access Weak_Data; Atomic : Boolean) is begin if Atomic then Increment (R.Refcount); else Unsafe_Increment (R.Refcount); end if; end Inc_Ref; -------------- -- Finalize -- -------------- procedure Finalize (Data : in out Weak_Data_Access; Atomic : Boolean) is begin if Atomic then if Decrement (Data.Refcount) then Unchecked_Free (Data); end if; else if Unsafe_Decrement (Data.Refcount) then Unchecked_Free (Data); end if; end if; end Finalize; --------------------- -- Shared_Pointers -- --------------------- package body Shared_Pointers is use type Pools.Element_Access; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Element_Type, Pools.Element_Access); pragma Warnings (Off, "*possible aliasing problem*"); function Convert is new Ada.Unchecked_Conversion (Pools.Element_Access, System.Address); function Convert is new Ada.Unchecked_Conversion (System.Address, Pools.Element_Access); pragma Warnings (On, "*possible aliasing problem*"); --------- -- Set -- --------- procedure Set (Self : in out Ref'Class; Data : Element_Type) is R : access Counters; begin Finalize (Self); Self.Data := new Element_Type'(Data); -- uses storage pool R := Pools.Header_Of (Self.Data); R.Refcount := 1; R.Weak_Data := null; end Set; ------------------- -- Unchecked_Get -- ------------------- function Unchecked_Get (Self : Ref'Class) return Element_Access is begin return Self.Data; end Unchecked_Get; ------------- -- Process -- ------------- procedure Process (Self : Ref'Class; Process : not null access procedure (E : Element_Type)) is begin Process (Self.Data.all); end Process; ------------- -- Is_Null -- ------------- function Is_Null (Self : Ref'Class) return Boolean is begin return Self.Data = null; end Is_Null; ---------- -- Weak -- ---------- function Weak (Self : Ref'Class) return Weak_Ref is R : Counters_Access; V : Weak_Data_Access; begin if Self.Data = null then return Null_Weak_Ref; end if; R := Pools.Header_Of (Self.Data); if R.Weak_Data = null then V := new Weak_Data' (Refcount => 2, -- hold by Self and the result Lock => 0, Element => Convert (Self.Data)); if not Sync_Bool_Compare_And_Swap (R.Weak_Data'Access, Oldval => null, Newval => V) then -- Was set by another thread concurrently Unchecked_Free (V); -- Need to increase refcount for the old weak ref Inc_Ref (R.Weak_Data, Atomic_Counters); end if; else Inc_Ref (R.Weak_Data, Atomic_Counters); end if; return (Controlled with Data => R.Weak_Data); end Weak; --------- -- Set -- --------- procedure Set (Self : in out Ref'Class; Weak : Weak_Ref'Class) is Data : Pools.Element_Access; WD : Weak_Data_Access := Weak.Data; NL : Atomic_Counter; begin Finalize (Self); if WD = null then return; end if; Data := Convert (WD.Element); if Data = null then return; end if; if Integer (Sync_Add_And_Fetch (WD.Lock'Access, 2)) rem 2 /= 0 then return; end if; if Inc_Ref (Pools.Header_Of (Data), Atomic_Counters) then Self.Data := Data; end if; NL := Sync_Sub_And_Fetch (WD.Lock'Access, 2); pragma Assert (Integer (NL) rem 2 = 0, "Unexpected Lock value " & NL'Img); end Set; --------------- -- Was_Freed -- --------------- function Was_Freed (Self : Weak_Ref'Class) return Boolean is begin return Self.Data = null or else Self.Data.Element = System.Null_Address; end Was_Freed; --------- -- "=" -- --------- overriding function "=" (P1, P2 : Ref) return Boolean is begin return P1.Data = P2.Data; end "="; ------------ -- Adjust -- ------------ overriding procedure Adjust (Self : in out Ref) is RC : Atomic_Counter; begin if Self.Data /= null then RC := Inc_Ref (Pools.Header_Of (Self.Data), Atomic_Counters); pragma Assert (RC > 1, "Unexpected reference counter after adjust" & RC'Img); end if; end Adjust; ------------ -- Adjust -- ------------ overriding procedure Adjust (Self : in out Weak_Ref) is begin if Self.Data /= null then Inc_Ref (Self.Data, Atomic_Counters); end if; end Adjust; -------------- -- Finalize -- -------------- overriding procedure Finalize (Self : in out Weak_Ref) is begin if Self.Data /= null then Finalize (Self.Data, Atomic_Counters); -- Make Finalize idempotent, since it could be called several -- times for the same instance (RM 7.6.1(24)). Self.Data := null; end if; end Finalize; -------------- -- Finalize -- -------------- overriding procedure Finalize (Self : in out Ref) is R : Counters_Access; Data : Pools.Element_Access := Self.Data; begin if Data /= null then Self.Data := null; R := Pools.Header_Of (Data); if (if Atomic_Counters then Decrement (R.Refcount) else Unsafe_Decrement (R.Refcount)) then if R.Weak_Data /= null then R.Weak_Data.Element := Null_Address; -- Spinlock to wait until all Set Ref from Weak_Ref -- operations completed. while R.Weak_Data.Lock /= 0 or else not Sync_Bool_Compare_And_Swap_Counter (R.Weak_Data.Lock'Access, 0, 1) loop -- Would be better to use GCC _mm_pause instruction -- instead of zero delay but it is not supported in GCC -- for all platforms. delay 0.0; end loop; Finalize (R.Weak_Data, Atomic_Counters); end if; Release (Data.all); Unchecked_Free (Data); -- using storage_pool end if; end if; end Finalize; ------------------ -- Get_Refcount -- ------------------ function Get_Refcount (Self : Ref'Class) return Natural is begin if Self.Data = null then return 0; else return Natural (Pools.Header_Of (Self.Data).Refcount); end if; end Get_Refcount; ------------------ -- From_Element -- ------------------ procedure From_Element (Self : out Ref'Class; Element : Element_Access) is begin if Self.Data /= Element then Finalize (Self); Self.Data := Element; Adjust (Self); end if; end From_Element; end Shared_Pointers; -------------------- -- Smart_Pointers -- -------------------- package body Smart_Pointers is --------- -- Set -- --------- procedure Set (Self : in out Ref; Data : access Encapsulated'Class) is begin if Self.Data = Refcounted_Access (Data) then -- Avoid finalizing Self.Data if we are going to reuse it return; end if; Finalize (Self); -- decrement reference count Self.Data := Refcounted_Access (Data); Adjust (Self); -- increment reference count if needed end Set; --------- -- Set -- --------- procedure Set (Self : in out Ref; Data : Encapsulated'Class) is Tmp : constant Encapsulated_Access := new Encapsulated'Class'(Data); begin Set (Self, Tmp); end Set; --------- -- Get -- --------- function Get (P : Ref) return Encapsulated_Access is begin return Encapsulated_Access (P.Data); end Get; --------- -- "=" -- --------- overriding function "=" (P1, P2 : Ref) return Boolean is begin return P1.Data = P2.Data; end "="; -------------- -- Finalize -- -------------- overriding procedure Finalize (P : in out Ref) is Data : Refcounted_Access := P.Data; begin -- Make Finalize idempotent, since it could be called several -- times for the same instance (RM 7.6.1(24)). P.Data := null; -- Test if refcount is > 0, in case we are already freeing this -- element. if Data /= null then if Decrement (Data.Refcount) then Free (Data.all); Unchecked_Free (Data); end if; end if; end Finalize; ------------ -- Adjust -- ------------ overriding procedure Adjust (P : in out Ref) is begin if P.Data /= null then Sync_Add_And_Fetch (P.Data.Refcount'Access, 1); end if; end Adjust; ------------------ -- Get_Refcount -- ------------------ function Get_Refcount (Self : Ref) return Natural is begin if Self.Data = null then return 0; else return Natural (Self.Data.Refcount); end if; end Get_Refcount; end Smart_Pointers; end GNATCOLL.Refcount; gnatcoll-core-21.0.0/src/gnatcoll-json-utility.ads0000644000175000017500000000464713743647711021750 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2011-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ private package GNATCOLL.JSON.Utility is JsonMimeType : constant String := "application/json"; function Escape_Non_Print_Character (C : Wide_Wide_Character) return String; function Escape_String (Text : UTF8_XString) return Ada.Strings.Unbounded.Unbounded_String; -- Translates an UTF-8 encoded unbounded string into a JSON-escaped string function Un_Escape_String (Text : String; Low : Natural; High : Natural) return UTF8_XString; -- Translates a JSON-escaped string into an UTF-8 encoded unbounded string -- Low represents the lower bound of the JSON string in Text -- High represents the higher bound of the JSON string in Text end GNATCOLL.JSON.Utility; gnatcoll-core-21.0.0/src/gnatcoll-mmap-system__unix.adb0000644000175000017500000002260713661715457022731 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2007-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.IO_Exceptions; with Ada.Unchecked_Conversion; with Interfaces.C; with System; use System; with GNAT.OS_Lib; use GNAT.OS_Lib; package body GNATCOLL.Mmap.System is type Mmap_Prot is mod Interfaces.C.int'Last; for Mmap_Prot'Size use Interfaces.C.int'Size; -- PROT_NONE : constant Mmap_Prot := 16#00#; -- PROT_EXEC : constant Mmap_Prot := 16#04#; PROT_READ : constant Mmap_Prot := 16#01#; PROT_WRITE : constant Mmap_Prot := 16#02#; type Mmap_Flags is mod Interfaces.C.int'Last; for Mmap_Flags'Size use Interfaces.C.int'Size; -- MAP_NONE : constant Mmap_Flags := 16#00#; -- MAP_FIXED : constant Mmap_Flags := 16#10#; MAP_SHARED : constant Mmap_Flags := 16#01#; MAP_PRIVATE : constant Mmap_Flags := 16#02#; function From_Advice is new Ada.Unchecked_Conversion (Use_Advice, Interfaces.C.int); function Mmap (Start : Standard.System.Address := Null_Address; Length : File_Size; Prot : Mmap_Prot := PROT_READ; Flags : Mmap_Flags := MAP_PRIVATE; Fd : GNAT.OS_Lib.File_Descriptor; Offset : File_Size := 0) return Standard.System.Address; pragma Import (C, Mmap, "gnatcoll_mmap"); function Munmap (Start : Standard.System.Address; Length : File_Size) return Integer; pragma Import (C, Munmap, "gnatcoll_munmap"); procedure Madvise (Addr : Standard.System.Address; Length : File_Size; Advice : Use_Advice) with Inline; -- Allows a process that has knowledge of its memory behavior to -- describe it to the system. This advice applies to the mapped -- region at address Addr, and for the given Length. If Length -- is 0, this applies to the whole region. function Align (Addr : File_Size) return File_Size; -- Align some offset/length to the lowest page boundary function Is_Mapping_Available return Boolean; -- Wheter memory mapping is actually available on this system. It is an -- error to use Create_Mapping and Dispose_Mapping if this is False. ------------- -- Madvise -- ------------- procedure Madvise (Addr : Standard.System.Address; Length : File_Size; Advice : Use_Advice) is procedure Internal (Addr : Standard.System.Address; Length : File_Size; Advice : Interfaces.C.int); pragma Import (C, Internal, "gnatcoll_madvise"); begin Internal (Addr, Length, From_Advice (Advice)); end Madvise; --------------- -- Open_Read -- --------------- function Open_Read (Filename : String; Use_Mmap_If_Available : Boolean := True) return System_File is Fd : constant File_Descriptor := Open_Read (Filename, Binary); begin if Fd = Invalid_FD then raise Ada.IO_Exceptions.Name_Error with "Cannot open " & Filename; end if; return (Fd => Fd, Mapped => Use_Mmap_If_Available and then Is_Mapping_Available, Write => False, Length => File_Size (File_Length (Fd))); end Open_Read; ---------------- -- Open_Write -- ---------------- function Open_Write (Filename : String; Use_Mmap_If_Available : Boolean := True) return System_File is Fd : constant File_Descriptor := Open_Read_Write (Filename, Binary); begin if Fd = Invalid_FD then raise Ada.IO_Exceptions.Name_Error with "Cannot open " & Filename; end if; return (Fd => Fd, Mapped => Use_Mmap_If_Available and then Is_Mapping_Available, Write => True, Length => File_Size (File_Length (Fd))); end Open_Write; ----------- -- Close -- ----------- procedure Close (File : in out System_File) is begin Close (File.Fd); File.Fd := Invalid_FD; end Close; -------------------- -- Read_From_Disk -- -------------------- function Read_From_Disk (File : System_File; Offset, Length : File_Size) return GNAT.Strings.String_Access is Buffer : GNAT.Strings.String_Access := new String (1 .. Integer (Length)); begin -- ??? Lseek offset should be a size_t instead of a Long_Integer Lseek (File.Fd, Long_Integer (Offset), Seek_Set); if GNAT.OS_Lib.Read (File.Fd, Buffer.all'Address, Integer (Length)) /= Integer (Length) then GNAT.Strings.Free (Buffer); raise Ada.IO_Exceptions.Device_Error; end if; return Buffer; end Read_From_Disk; ------------------- -- Write_To_Disk -- ------------------- procedure Write_To_Disk (File : System_File; Offset, Length : File_Size; Buffer : GNAT.Strings.String_Access) is begin pragma Assert (File.Write); Lseek (File.Fd, Long_Integer (Offset), Seek_Set); if GNAT.OS_Lib.Write (File.Fd, Buffer.all'Address, Integer (Length)) /= Integer (Length) then raise Ada.IO_Exceptions.Device_Error; end if; end Write_To_Disk; -------------------- -- Create_Mapping -- -------------------- procedure Create_Mapping (File : System_File; Offset, Length : in out File_Size; Mutable : Boolean; Mapping : out System_Mapping; Advice : Use_Advice := Use_Normal) is Prot : Mmap_Prot; Flags : Mmap_Flags; begin if File.Write then Prot := PROT_READ or PROT_WRITE; Flags := MAP_SHARED; else Prot := PROT_READ; if Mutable then Prot := Prot or PROT_WRITE; end if; Flags := MAP_PRIVATE; end if; -- Adjust offset and mapping length to account for the required -- alignment of offset on page boundary. declare Queried_Offset : constant File_Size := Offset; begin Offset := Align (Offset); -- First extend the length to compensate the offset shift, then align -- it on the upper page boundary, so that the whole queried area is -- covered. Length := Length + Queried_Offset - Offset; Length := Align (Length + Get_Page_Size - 1); end; Mapping := (Address => Mmap (Offset => Offset, Length => Length, Prot => Prot, Flags => Flags, Fd => File.Fd), Length => Length); if Advice /= Use_Normal then Madvise (Mapping.Address, Length => 0, Advice => Advice); end if; end Create_Mapping; --------------------- -- Dispose_Mapping -- --------------------- procedure Dispose_Mapping (Mapping : in out System_Mapping) is Ignored : Integer; pragma Unreferenced (Ignored); begin Ignored := Munmap (Mapping.Address, Mapping.Length); Mapping := Invalid_System_Mapping; end Dispose_Mapping; -------------------------- -- Is_Mapping_Available -- -------------------------- function Is_Mapping_Available return Boolean is function Has_Mmap return Integer; pragma Import (C, Has_Mmap, "gnatcoll_has_mmap"); begin return Has_Mmap /= 0; end Is_Mapping_Available; ------------------- -- Get_Page_Size -- ------------------- function Get_Page_Size return File_Size is function Internal return Integer; pragma Import (C, Internal, "getpagesize"); begin return File_Size (Internal); end Get_Page_Size; ----------- -- Align -- ----------- function Align (Addr : File_Size) return File_Size is begin return Addr - Addr mod Get_Page_Size; end Align; end GNATCOLL.Mmap.System; gnatcoll-core-21.0.0/src/gnatcoll-config.ads0000644000175000017500000003056713743647711020543 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides a general handling mechanism for config files. -- Any format of config files can be supported. The default implementation -- provides support for Windows-like .ini files, that is: -- -- # Comment -- [Section] -- key1 = value1 -- key2 = value2 -- -- .ini file format is not a very strict parser. Statement types are -- considered in that order: -- -- 1- comment -- 2- assignment -- 3- section declaration -- -- Thus for example: -- -- [key1 = key2] -- -- Will be parsed as "[key1" = "key2]" and not as a section declaration. -- -- Any line that does not correspond to any of the previous types will be -- ignored. Leading and trailing whitespaces are ignored. Whitespaces around -- the first '=' are also ignored. -- -- As a special case, some strings are automatically substituted in the values -- - HOME: home directory for the user, as configured in the parser -- -- This package is build through several layers of tagged objects: -- - the first layer provides the parsing of config files, and through a -- callback returns the (key, value) pairs to the application -- - the second layer provides a pool of these pairs, ie provides the storage -- on top of the first layer. Queries are done through strings -- - a third layer makes the keys as real objects, so that you build the key -- once, and then query the value from it directly. This is mostly syntactic -- sugar, although it helps ensure that you are always reading existing keys -- (if your coding convention forces users to use these keys). private with Ada.Containers.Indefinite_Hashed_Maps; private with Ada.Strings.Hash; private with GNATCOLL.Strings; with GNATCOLL.VFS; package GNATCOLL.Config is -------------------------- -- Parsing config files -- -------------------------- type Config_Parser is abstract tagged private; -- Abstract type for all config streams (files, in-memory,...), with any -- format. Concrete types below will provide the actual implementation. -- Typical usage looks like: -- declare -- C : File_Config_Parser; -- begin -- Open (C, "filename.txt"); -- while not C.At_End loop -- Put_Line (C.Key & " = " & C.Value); -- C.Next; -- end loop; -- end; function At_End (Self : Config_Parser) return Boolean is abstract; -- Whether the config parsing is at the end procedure Next (Self : in out Config_Parser) is abstract; -- Move to the next (key, value) in the configuration. Before that call, -- the parser is left on the first value in the configuration. function Section (Self : Config_Parser) return String is abstract; function Key (Self : Config_Parser) return String is abstract; function Value (Self : Config_Parser) return String is abstract; -- Return the current (section, key, value); procedure Set_System_Id (Self : in out Config_Parser; System_ID : String); -- Sets the system ID for the config. -- If the config is found in a file, this should be the absolute path name -- to that file. This will generally be called automatically when opening -- the file. -- This system id is used to resolve absolute file names. function As_Integer (Self : Config_Parser) return Integer; function As_Boolean (Self : Config_Parser) return Boolean; function As_Absolute_File (Self : Config_Parser) return String; function As_Absolute_Dir (Self : Config_Parser) return String; -- Assuming the current value is a file or directory, converts it to an -- absolute name, where relative paths are resolved relative to the -- config's system_id. -- These will raise Constraint_Error if used on non-matching values. ----------------- -- File config -- ----------------- type File_Config_Parser is abstract new Config_Parser with private; -- A special implementation for config streams based on actual files procedure Open (Self : in out File_Config_Parser; Filename : String); -- Open a file overriding function At_End (Self : File_Config_Parser) return Boolean; --------------- -- INI files -- --------------- type INI_Parser is new File_Config_Parser with private; -- a special parser for Windows' .ini files procedure Configure (Self : in out INI_Parser; Comment_Start : String := "#"; Handles_Sections : Boolean := True; Home : String := ""); -- "Home" is the substitution pattern for "HOME" in the values. If -- unspecified, it is computed automatically. overriding procedure Open (Self : in out INI_Parser; Filename : String); overriding procedure Next (Self : in out INI_Parser); overriding function Section (Self : INI_Parser) return String; overriding function Key (Self : INI_Parser) return String; overriding function Value (Self : INI_Parser) return String; ------------------- -- Resource pool -- ------------------- type Config_Pool is tagged private; -- This type provides storage for a config file procedure Set_System_Id (Self : in out Config_Pool; System_ID : String); -- Set the absolute name used to resolve file names in Get_File procedure Fill (Self : in out Config_Pool; Config : in out Config_Parser'Class); -- Load all keys from Config, and store the (key, value) pairs in Self. -- Multiple files can be merged into the same pool. -- Set_System_Id is automatically called, thus file names will be resolved -- relative to the last Config loaded in the pool. Section_From_Key : constant String; -- Indicates that the section should in fact be read from the key (as -- opposed to being specified separately). In this case, the key is split -- at the first "." (if there is none, the section name is empty). -- For instance: "section1.key1" or "section1.key2". -- -- It is often more convenient to specify the section that way, in exchange -- for a small performance penalty and a possible ambiguity if the key -- itself contains a ".", which is not recommended. Whole_Value : constant Natural := 0; function Get (Self : Config_Pool; Key : String; Section : String := Section_From_Key; Index : Natural := Whole_Value) return String; -- Return the value associated with Key. -- Index is used for comma-separated lists of values, and will retrieve -- one of the specific elements of the list. The whole value (no splitting) -- is returned if Index is Whole_Value. The empty string is returned if -- there is no such item in the list function Get_Integer (Self : Config_Pool; Key : String; Section : String := Section_From_Key; Index : Natural := Whole_Value) return Integer; function Get_Boolean (Self : Config_Pool; Key : String; Section : String := Section_From_Key; Index : Natural := Whole_Value) return Boolean; function Get_File (Self : Config_Pool; Key : String; Section : String := Section_From_Key; Index : Natural := Whole_Value) return String; -- Same as above, but returns an absolute filename. Relative paths are -- resolved relative to the config location where Key was declared. function To_File (Self : Config_Pool; Key : String; Section : String := Section_From_Key; Value : String) return GNATCOLL.VFS.Virtual_File; -- Converts value to a file. It is relative to the location of the config -- file that provided Key. This is similar to calling Get_File directly, -- but is useful in contexts where you need to first manipulate the value -- read from the config and then interpret it as a file. procedure Set (Self : in out Config_Pool; Section, Key, Value : String); -- Override a specific key -------------------------------- -- Resource pool, static keys -- -------------------------------- type Config_Key is tagged private; function Create (Key : String; Section : String := "") return Config_Key; -- Create a new config key function Get (Self : Config_Key; Conf : Config_Pool'Class; Index : Natural := Whole_Value) return String; function Get_Integer (Self : Config_Key; Conf : Config_Pool'Class; Index : Natural := Whole_Value) return Integer; function Get_Boolean (Self : Config_Key; Conf : Config_Pool'Class; Index : Natural := Whole_Value) return Boolean; function Get_File (Self : Config_Key; Conf : Config_Pool'Class; Index : Natural := Whole_Value) return String; function To_File (Self : Config_Key; Conf : Config_Pool'Class; Value : String) return GNATCOLL.VFS.Virtual_File; -- Read the key from the configuration. -- Using this API might help ensure that you are always accessing existing -- keys. In this case, you would have a global package that defines all -- valid keys: -- -- Key1 : constant Config_Key := Create ("..."); -- Key2 : constant Config_Key := Create ("..."); -- -- Then your coding standard should specify that you can only access the -- configuration via those keys: -- -- Put_Line (Key1.Get); -- -- There is therefore no possible typo in the name of the key, and if you -- rename the key in the configuration file, you have a single place to -- change. private Section_From_Key : constant String := "="; -- Choose = as special name as it cannot appears in a section name. type Config_Parser is abstract tagged record System_ID : Strings.XString; end record; type File_Config_Parser is abstract new Config_Parser with record Contents : Strings.XString; First : Integer := Integer'Last; end record; type INI_Parser is new File_Config_Parser with record Equal, Eol : Integer; Current_Section : Strings.XString; Comment_Start : Strings.XString := Strings.To_XString ("#"); Use_Sections : Boolean := True; Home : VFS.Virtual_File := VFS.Get_Home_Directory; end record; type Config_Value (Len : Natural) is record System_ID : Strings.XString; Value : String (1 .. Len); end record; package String_Maps is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => String, -- "section=key" Element_Type => Config_Value, Hash => Ada.Strings.Hash, Equivalent_Keys => "=", "=" => "="); type Config_Key is tagged record Section, Key : Strings.XString; end record; type Config_Pool is tagged record System_ID : Strings.XString; Keys : String_Maps.Map; end record; end GNATCOLL.Config; gnatcoll-core-21.0.0/src/gnatcoll-os-constants__windows.ads0000644000175000017500000000560113661715457023633 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L . O S . C O N S T A N T S -- -- -- -- Copyright (C) 2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This is the Windows version of GNATCOLL.OS.Constants package package GNATCOLL.OS.Constants is pragma Pure; ----------------------- -- OS identification -- ----------------------- OS : constant OS_Type := Windows; ------------------------------------- -- File system specific constants -- ------------------------------------- Dir_Sep : constant Character := '\'; -- The character that separates qualified filename components Path_Sep : constant Character := ';'; -- The character that separates paths in a path list Exe_Ext : constant String := ".exe"; -- Executable image extension Default_Casing_Policy : constant Filename_Casing_Policy := Preserving; -- Default casing policy chosen by the OS ------------------------------------------------ -- Dynamic link libraries specific constants -- ------------------------------------------------ DLL_Name : constant String := "DLL"; -- The OS-specific term to refer to a DLL DLL_Search_Path_Var : constant String := "PATH"; -- Environment variable used to search for DLLs DLL_Ext : constant String := ".dll"; -- DLL image extension end GNATCOLL.OS.Constants; gnatcoll-core-21.0.0/src/gnatcoll-atomic.adb0000644000175000017500000001514713661715457020530 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Interfaces; use Interfaces; package body GNATCOLL.Atomic is function Intrinsic_Sync_Bool_Compare_And_Swap (Ptr : access Atomic_Counter; Oldval : Atomic_Counter; Newval : Atomic_Counter) return Boolean; pragma Import (Intrinsic, Intrinsic_Sync_Bool_Compare_And_Swap, External_Name => "__sync_bool_compare_and_swap_4"); function Intrinsic_Sync_Val_Compare_And_Swap (Ptr : access Atomic_Counter; Oldval : Atomic_Counter; Newval : Atomic_Counter) return Atomic_Counter; pragma Import (Intrinsic, Intrinsic_Sync_Val_Compare_And_Swap, External_Name => "__sync_val_compare_and_swap_4"); function Intrinsic_Sync_Add_And_Fetch (Ptr : access Atomic_Counter; Value : Atomic_Counter) return Atomic_Counter; pragma Import (Intrinsic, Intrinsic_Sync_Add_And_Fetch, External_Name => "__sync_add_and_fetch_4"); function Intrinsic_Sync_Sub_And_Fetch (Ptr : access Atomic_Counter; Value : Atomic_Counter) return Atomic_Counter; pragma Import (Intrinsic, Intrinsic_Sync_Sub_And_Fetch, External_Name => "__sync_sub_and_fetch_4"); ------------------ -- Is_Lock_Free -- ------------------ function Is_Lock_Free return Boolean is (True); ------------------------ -- Sync_Add_And_Fetch -- ------------------------ function Sync_Add_And_Fetch (Ptr : access Atomic_Counter; Value : Atomic_Counter) return Atomic_Counter is begin return Intrinsic_Sync_Add_And_Fetch (Ptr, Value); end Sync_Add_And_Fetch; procedure Sync_Add_And_Fetch (Ptr : access Atomic_Counter; Value : Atomic_Counter) is Dummy : Atomic_Counter with Unreferenced; begin Dummy := Intrinsic_Sync_Add_And_Fetch (Ptr, Value); end Sync_Add_And_Fetch; ------------------------ -- Sync_Sub_And_Fetch -- ------------------------ function Sync_Sub_And_Fetch (Ptr : access Atomic_Counter; Value : Atomic_Counter) return Atomic_Counter is begin return Intrinsic_Sync_Sub_And_Fetch (Ptr, Value); end Sync_Sub_And_Fetch; procedure Sync_Sub_And_Fetch (Ptr : access Atomic_Counter; Value : Atomic_Counter) is Dummy : Atomic_Counter with Unreferenced; begin Dummy := Intrinsic_Sync_Sub_And_Fetch (Ptr, Value); end Sync_Sub_And_Fetch; --------------- -- Increment -- --------------- procedure Increment (Value : aliased in out Atomic_Counter) is begin System.Atomic_Counters.Increment (Value); end Increment; --------------- -- Decrement -- --------------- procedure Decrement (Value : aliased in out Atomic_Counter) is begin System.Atomic_Counters.Decrement (Value); end Decrement; function Decrement (Value : aliased in out Atomic_Counter) return Boolean is begin return System.Atomic_Counters.Decrement (Value); end Decrement; ---------------------- -- Unsafe_Increment -- ---------------------- procedure Unsafe_Increment (Value : in out Atomic_Counter) is begin Value := Atomic_Counter'Succ (Value); end Unsafe_Increment; ---------------------- -- Unsafe_Decrement -- ---------------------- function Unsafe_Decrement (Value : in out Atomic_Counter) return Boolean is begin Value := Atomic_Counter'Pred (Value); return Value = 0; end Unsafe_Decrement; -------------------------------- -- Sync_Bool_Compare_And_Swap -- -------------------------------- function Sync_Bool_Compare_And_Swap (Ptr : access Element_Access; Oldval : Element_Access; Newval : Element_Access) return Boolean is function Intrinsic_Sync_Bool_And_Swap_Access (Ptr : access Element_Access; Oldval, Newval : Element_Access) return Interfaces.Integer_8; pragma Import (Intrinsic, Intrinsic_Sync_Bool_And_Swap_Access, External_Name => "gnatcoll_sync_bool_compare_and_swap_access"); begin return Intrinsic_Sync_Bool_And_Swap_Access (Ptr, Oldval, Newval) /= 0; end Sync_Bool_Compare_And_Swap; ---------------------------------------- -- Sync_Bool_Compare_And_Swap_Counter -- ---------------------------------------- function Sync_Bool_Compare_And_Swap_Counter (Ptr : access Atomic_Counter; Oldval : Atomic_Counter; Newval : Atomic_Counter) return Boolean is begin return Intrinsic_Sync_Bool_Compare_And_Swap (Ptr, Oldval, Newval); end Sync_Bool_Compare_And_Swap_Counter; --------------------------------------- -- Sync_Val_Compare_And_Swap_Counter -- --------------------------------------- function Sync_Val_Compare_And_Swap_Counter (Ptr : access Atomic_Counter; Oldval : Atomic_Counter; Newval : Atomic_Counter) return Atomic_Counter is begin return Intrinsic_Sync_Val_Compare_And_Swap (Ptr, Oldval, Newval); end Sync_Val_Compare_And_Swap_Counter; end GNATCOLL.Atomic; gnatcoll-core-21.0.0/src/gnatcoll-atomic.ads0000644000175000017500000001363513661715457020551 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides a number of low-level primitives to execute -- task-safe operations. -- When possible, these operations are executed via one of the intrinsic -- atomic operations of the compiler (generally implemented with special -- support from the CPU). with System.Atomic_Counters; package GNATCOLL.Atomic is subtype Atomic_Counter is System.Atomic_Counters.Atomic_Unsigned; Minus_One : constant Atomic_Counter := System.Atomic_Counters."-" (0, 1); function Is_Lock_Free return Boolean; -- Whether the implementation uses the processor's atomic operations -- or falls back on using locks function Sync_Add_And_Fetch (Ptr : access Atomic_Counter; Value : Atomic_Counter) return Atomic_Counter with Inline_Always; -- Increment Ptr by Value. This is task safe (either using a lock or -- intrinsic atomic operations). Returns the new value (as set, it -- might already have been changed by another task by the time this -- function returns. function Sync_Sub_And_Fetch (Ptr : access Atomic_Counter; Value : Atomic_Counter) return Atomic_Counter with Inline_Always; -- Decrement Ptr by Value. procedure Sync_Add_And_Fetch (Ptr : access Atomic_Counter; Value : Atomic_Counter) with Inline_Always; procedure Sync_Sub_And_Fetch (Ptr : access Atomic_Counter; Value : Atomic_Counter) with Inline_Always; -- Same as above, but ignore the return value. procedure Increment (Value : aliased in out Atomic_Counter) with Inline_Always; procedure Decrement (Value : aliased in out Atomic_Counter) with Inline_Always; function Decrement (Value : aliased in out Atomic_Counter) return Boolean with Inline_Always; -- Similar to the Sync_Add_And_Fetch and Sync_Sub_And_And, but -- always increment or decrement by one. -- On some systems (x86) this uses faster assembly instructions. -- Decrement returns True if the value reaches 0. function "+" (Left, Right : Atomic_Counter) return Atomic_Counter is abstract; function "-" (Left, Right : Atomic_Counter) return Atomic_Counter is abstract; -- Prevent standard operations on these counters function Unsafe_Decrement (Value : in out Atomic_Counter) return Boolean with Inline_Always; procedure Unsafe_Increment (Value : in out Atomic_Counter) with Inline_Always; -- These are unsafe operations. If you have two threads, and they all try -- to do "Unsafe_Add (A, 2)" at the same time, when A was initially 0, -- you could end up with the following values in A: -- 2 (both threads have read 0, then added 2) -- 4 (thread 1 has read and incremented, then thread 2) -- If you use the other operations above, you always end up with 4. function ">" (Left, Right : Atomic_Counter) return Boolean is (System.Atomic_Counters.">" (Left, Right)); -- Compare two counters. -- Note that by the time this function returns, and in a multi threaded -- application, either of the two counters might have changed. function "=" (Left, Right : Atomic_Counter) return Boolean renames System.Atomic_Counters."="; -- Make the operator visible generic type Element_Type (<>) is limited private; type Element_Access is access Element_Type; function Sync_Bool_Compare_And_Swap (Ptr : access Element_Access; Oldval : Element_Access; Newval : Element_Access) return Boolean; -- If Ptr is equal to Oldval, set it to Newval and return True. -- Otherwise, return False and do not modify the current value. -- This operation is task safe and atomic. function Sync_Bool_Compare_And_Swap_Counter (Ptr : access Atomic_Counter; Oldval : Atomic_Counter; Newval : Atomic_Counter) return Boolean; function Sync_Val_Compare_And_Swap_Counter (Ptr : access Atomic_Counter; Oldval : Atomic_Counter; Newval : Atomic_Counter) return Atomic_Counter; -- A version that works with Atomic_Counter. -- Ptr.all is set to Newval if and only if it is currently set to Oldval. -- Returns True if the value was changed. -- The second version returns the initial value of Ptr.all end GNATCOLL.Atomic; gnatcoll-core-21.0.0/src/gnatcoll-plugins__unix.adb0000644000175000017500000000627213661715457022136 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with System; use System; with Interfaces.C.Strings; use Interfaces; package body GNATCOLL.Plugins is ---------- -- Load -- ---------- function Load (Path : String) return Plugin is function dlopen (Lib_Name : String; Mode : C.int) return Plugin; pragma Import (C, dlopen, "dlopen"); RTLD_LAZY : constant := 1; C_Path : constant String := Path & ASCII.NUL; begin return dlopen (C_Path, RTLD_LAZY); end Load; --------------------- -- Routine_Address -- --------------------- function Routine_Address (P : Plugin; Name : String) return Address is function dlsym (Handle : Plugin; Sym_Name : String) return Address; pragma Import (C, dlsym, "dlsym"); C_Name : constant String := Name & ASCII.NUL; begin return dlsym (Handle => P, Sym_Name => C_Name); end Routine_Address; ------------------------ -- Last_Error_Message -- ------------------------ function Last_Error_Message return String is function dlerror return C.Strings.chars_ptr; pragma Import (C, dlerror, "dlerror"); begin return C.Strings.Value (dlerror); end Last_Error_Message; ------------ -- Unload -- ------------ procedure Unload (P : in out Plugin) is function dlclose (Handle : Plugin) return C.int; pragma Import (C, dlclose, "dlclose"); Ignored : C.int; pragma Unreferenced (Ignored); begin Ignored := dlclose (P); end Unload; end GNATCOLL.Plugins; gnatcoll-core-21.0.0/src/gnatcoll-string_builders.ads0000644000175000017500000001325113661715457022466 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with System; package GNATCOLL.String_Builders is type String_Builder is limited private; -- String_Builder is an efficient unbounded structure to create String -- object by aggregation. The structure also maintains a null character at -- the end of the String allowing export to C without reallocation. -- Instances of String_Builder should be finalized by calling Deallocate -- procedure. type CString is private; -- CString can be used as parameter to C functions expecting a -- "const char *" procedure Append (Self : in out String_Builder; Str : String); -- Append Str to Self procedure Append (Self : in out String_Builder; Char : Character); -- Append Char to Self procedure Set (Self : in out String_Builder; Str : String); -- Reset content of Self to Str function Element (Self : String_Builder; N : Positive) return Character with Inline; -- Return the Nth character of Self function Length (Self : String_Builder) return Natural with Inline; -- Return the length of Self (the size does not take into account -- the trailing ASCII.NUL character maintained by the structure). function As_String (Self : String_Builder) return String with Inline; -- Return an Ada String (without the trailing ASCII.NUL) function As_CString (Self : String_Builder) return CString with Inline; -- Return a char* pointing to the beginning of Self content procedure Deallocate (Self : in out String_Builder) with Inline; -- Free heap memory associated with Self type Static_String_Builder (Size_With_NUL : Natural) is limited private; -- Behave the same way as String_Builder except that the maximum -- size if known in advance. The structure does not allocate memory -- on the heap. Size passed as discriminant should be the maximum size -- of the string plus one character for the trailing NUL char. procedure Append (Self : in out Static_String_Builder; Str : String) with Inline; -- Append Str to Self procedure Append (Self : in out Static_String_Builder; Char : Character) with Inline; -- Append Char to Self procedure Set (Self : in out Static_String_Builder; Str : String) with Inline; -- Reset content of Self to Str function Element (Self : Static_String_Builder; N : Positive) return Character with Inline; -- Return the Nth character of Self function Length (Self : Static_String_Builder) return Natural with Inline; -- Return the length of Self (the size does not take into account -- the trailing ASCII.NUL character maintained by the structure). function As_String (Self : Static_String_Builder) return String with Inline; -- Return an Ada String (without the trailing ASCII.NUL) function As_CString (Self : Static_String_Builder) return CString with Inline; -- Return a char* pointing to the beginning of Self content private type String_Access is access String; type CString is new System.Address; Empty_String : constant String := "" & ASCII.NUL; Empty_CString : constant CString := CString (Empty_String (1)'Address); type Static_String_Builder (Size_With_NUL : Natural) is limited record Str : String (1 .. Size_With_NUL); Str_Last : Natural := 0; end record; String_Builder_Short_Size : constant Natural := 43; type String_Builder is limited record Heap_Str : String_Access := null; Str_Last : Natural := 0; Stack_Str : String (1 .. String_Builder_Short_Size + 1); end record; -- String_Builder record size is set to use 64 bytes on most systems -- (size of L1 cache line on most systems). For 43-bytes long or smaller -- strings no allocation on the heap will be done (in that case Stack_Str -- is used to store the string). For bigger strings Heap_Str is used. -- Str_Last is the index either in Stack_Str or Heap_Str of the last -- character in the string. end GNATCOLL.String_Builders; gnatcoll-core-21.0.0/src/gnatcoll-path.ads0000644000175000017500000001301713661715457020223 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNATCOLL.VFS_Types; use GNATCOLL.VFS_Types; private package GNATCOLL.Path is Invalid_Filesystem : exception; -- Raised when calling any of the below methods with FS_Unknown ------------------- -- FS Properties -- ------------------- function Local_FS return FS_Type; pragma Inline (Local_FS); -- Get the local FS type function Is_Case_Sensitive (FS : FS_Type) return Boolean; -- Tell if FS is case sensitive function Has_Devices (FS : FS_Type) return Boolean; -- Tell if the Filesystem handles devices (hard disk letters for windows) function Multi_Unit_Index_Char (FS : FS_Type) return Character; -- The character used by GNAT when creating ALI files for multi-unit files -- on the given filesystem (this is generally '~' expect on VMS where it is -- set to '$'). function Exe_Extension (FS : FS_Type) return FS_String; -- .exe on Windows, nothing on Unix ------------------ -- Path queries -- ------------------ function Get_Root (FS : FS_Type; Path : FS_String) return FS_String; -- Return the root directory of the path function Is_Absolute_Path (FS : FS_Type; Path : FS_String) return Boolean; -- Tell whether the path is absolute ------------------------ -- Path manipulations -- ------------------------ function Path (FS : FS_Type; Device : FS_String; Dir : FS_String; File : FS_String) return FS_String; -- Return a path composed of Device, Dir, and File function Equal (FS : FS_Type; Path1, Path2 : FS_String) return Boolean; -- Tell if Path1 and Path2 are equivalent function To_Unix (FS : FS_Type; Path : FS_String; Cygwin_Path : Boolean := False) return FS_String; -- Translate a Path to unix style function From_Unix (FS : FS_Type; Path : FS_String) return FS_String; -- Translate a Path from unix style function File_Extension (FS : FS_Type; Path : FS_String) return FS_String; -- Return the file extension, including the last '.' function Base_Name (FS : FS_Type; Path : FS_String; Suffix : FS_String := "") return FS_String; -- Return the base file name function Base_Dir_Name (FS : FS_Type; Path : FS_String) return FS_String; -- Return the directory base name. Root directories will be returned -- as-is ("/", "C:\", "\\machine\service\") function Get_Parent (FS : FS_Type; Path : FS_String) return FS_String; -- Return the parent directory of Path. This differs from Dir_Name in that -- calling Get_Parent on a directory will return the directory's parent. function Dir_Name (FS : FS_Type; Path : FS_String) return FS_String; -- Return the directory path. Calling Dir_Name on a directory will return -- the directory itself. function Is_Dir_Name (FS : FS_Type; Path : FS_String) return Boolean; -- Return true if Path denotes a directory path in FS (e.g. ends with a -- directory separator). function Ensure_Directory (FS : FS_Type; Path : FS_String) return FS_String; -- Return a directory path from furnished path. -- On Windows, for a path C:\path\to, this will return C:\path\to\ -- On VMS, for a path disk:[path]to.dir, this will return disk:[path.to] function Device_Name (FS : FS_Type; Path : FS_String) return FS_String; -- Return the device of the path (if applicable). Empty string otherwise function Normalize (FS : FS_Type; Path : FS_String) return FS_String; -- Replace every ./ or ../ items of the path function Relative_Path (FS : FS_Type; Ref : FS_String; Path : FS_String) return FS_String; -- Return the path of Path relative to Ref end GNATCOLL.Path; gnatcoll-core-21.0.0/src/gnatcoll-utils.ads0000644000175000017500000003412413743647711020427 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- Various utility subprograms used in GNATCOLL, and that can easily be reused -- elsewhere pragma Ada_2012; with Ada.Calendar.Time_Zones; use Ada.Calendar; with Ada.Characters.Handling; with Ada.Strings.Unbounded; with GNAT.Calendar; with GNAT.Expect; with GNAT.Strings; package GNATCOLL.Utils is ------------- -- Strings -- ------------- -- Some simple-minded string manipulation routines. -- See also GNATCOLL.Strings providing alternative more efficient -- implementation type Cst_String_Access is access constant String; procedure Free (List : in out GNAT.Strings.String_List); -- Free the memory used by List. function Equal (S1, S2 : String; Case_Sensitive : Boolean) return Boolean; function Case_Insensitive_Equal (S1, S2 : String) return Boolean; pragma Inline (Equal, Case_Insensitive_Equal); -- Compare two strings function Image (Value : Integer; Min_Width : Integer; Force_Sign : Boolean := False; Padding : Character := '0') return String; -- Returns Value as a string, using at least Width digits (padded with -- leading characters Padding if necessary); negative values will always -- have a leading minus sign; positive values will have a leading plus sign -- if Force_Sign is True. -- If you set Min_Width to 1, the result is similar to 'Image, without the -- leading space for positive numbers. procedure Replace (S : in out Ada.Strings.Unbounded.Unbounded_String; Pattern : String; Replacement : String) with Pre => Pattern /= ""; -- Returns S, with all occurrences of Pattern replaced with Replacement function Replace (S : String; Pattern : String; Replacement : String) return String with Pre => Pattern /= ""; -- Returns S, with all occurrences of Pattern replaced with Replacement procedure Split (Str : String; On : String; For_Each : access function (Item : String) return Boolean); -- Splits the string on the given delimiter "On" and calls the function -- For_Each for every found substring not including the delimiter. -- If For_Each returns False the string processing stops. function Split (Str : String; On : Character; Omit_Empty_Lines : Boolean := True) return GNAT.Strings.String_List_Access; -- Splits the string on the given character. -- The result depends on the value of Omit_Empty_Lines. For instance, the -- string "a" & ASCII.LF & ASCII.LF & "b" will be split as: -- ["a", "b"] if Omit_Empty_Lines is true -- ["a", "", "b"] otherwise -- -- Result must be freed by caller. -- See also Split below type Unbounded_String_Array is array (Natural range <>) of Ada.Strings.Unbounded.Unbounded_String; Empty_Array : constant Unbounded_String_Array; function Split (Str : String; On : Character; Omit_Empty_Lines : Boolean := True) return Unbounded_String_Array; -- Same as Split above, returning an Unbounded_String_Array that does not -- need to be freed. function Capitalize (Name : String) return String; -- Capitalizes a string, i.e. puts in upper case the first character and -- any character preceded by '_' function Is_Whitespace (Char : Character) return Boolean; -- Returns True if Char is a space, new line, or tab; otherwise returns -- False. function Starts_With (Str : String; Prefix : String) return Boolean; -- Returns True if Str starts with Prefix function Ends_With (Str : String; Suffix : String) return Boolean; -- Returns True if Str ends with Suffix procedure Skip_Blanks (Str : String; Index : in out Natural); procedure Skip_Blanks_Backward (Str : String; Index : in out Natural); -- If Str(Index) is a white space, new line, or tab, then skip it and all -- following ones. On exit, Index points to the first non white space -- character, or after Str'Last. -- Skip_Blanks_Backward moves Index backward instead, and will leave it -- before Str'First if no non-whitespace was found. function Find_Char (Str : String; Char : Character) return Natural; -- Returns the first occurrence of Char after Str'First (use substrings for -- later occurrences). Returns Str'Last + 1 if there is no match function Join (Str : String; List : GNAT.Strings.String_List) return String; -- Returns a string that is the concatenation of the list elements, -- separated by Str: (List(1) & Str & List(2) & Str & ...) -- null elements in list are skipped function EOL (Str : String) return Natural; pragma Inline (EOL); -- Returns the first ASCII.LF character after Str'First (use substrings for -- subsequent lines). The result is either Str'Last+1 or points to the -- first ASCII.LF found. function Line_Start (Str : String; P : Natural) return Natural; -- Returns the start of the line pointed by P function Line_End (Str : String; P : Natural) return Natural; -- Returns the end of the line pointed by P procedure Skip_Lines (Str : String; Lines : Integer; Index : in out Natural; Lines_Skipped : out Natural); -- Skips lines forward or backward. Sets Index to the beginning of a line. -- Lines_Skipped is the number of lines that have actually been skipped. -- Use with Skip_To_Column to go to a specific position in a buffer. procedure Skip_To_Column (Str : String; Columns : Integer := 0; Index : in out Integer; Tab_Width : Integer := 8); -- Assuming Index points to the beginning of a line (as is the case after -- Skip_Lines for instance), jumps to the specific column on that line. -- This procedure handles tabulations (i.e. Columns are columns visible to -- the user following the tab expansion). function Forward_UTF8_Char (Str : String; Index : Integer) return Integer; -- Moves Index one character forward, taking into account UTF8 encoding. function Next_Line (Str : String; P : Natural) return Natural; -- Returns the start of the next line or Str'Last if the end of Str -- is reached without finding next line. function Previous_Line (Str : String; P : Natural) return Natural; -- Returns the start of the previous line or Str'First if P already -- points to the first line of Str. function Is_Blank_Line (Str : String; Index : Natural := 0) return Boolean; -- Returns True if the line pointed by Index only contains blank characters -- (' ', HT, LF, CR). By default, if Index is 0, then the line considered -- is the first line of the buffer. procedure Skip_To_String (Str : String; Index : in out Natural; Substring : String); -- Skips every character until an occurrence of Substring is found. -- Index is set to the first character of the occurrence. function Strip_Character (Text : String; C : Character) return String; -- Returns a version of Text after stripping all C's from the string function Strip_CR (Text : String) return String; pragma Inline (Strip_CR); -- Returns a version of Text after stripping all ASCII.CR from the string. -- This function is used on Windows or when the Strip_CR preference is -- enabled (for systems that share DOS files). -- CR/LF sequences are replaced by LF chars. function Predicate (Text : String; Predicate : access function (Item : Character) return Boolean) return Boolean is (for all C of Text => Predicate (C)); -- Whether all characters in Text match Predicate. -- This can be used with the various utilities in Ada.Characters.Handling, -- for instance to check whether a string is made up of only lower case -- characters. function Is_Alphanumeric (Text : String) return Boolean is (Predicate (Text, Ada.Characters.Handling.Is_Alphanumeric'Access)); function Is_Lower (Text : String) return Boolean is (Predicate (Text, Ada.Characters.Handling.Is_Lower'Access)); function Is_Upper (Text : String) return Boolean is (Predicate (Text, Ada.Characters.Handling.Is_Upper'Access)); function Is_Identifier (C : Character) return Boolean is (C = '_' or else Ada.Characters.Handling.Is_Alphanumeric (C)); function Is_Identifier (Text : String) return Boolean is (Predicate (Text, Is_Identifier'Access)); -- Whether C is a valid character for an identifier (in most programming -- languages). It doesn't check whether the identifier starts with an -- underscore for instance, just whether the characters would be valid. ------------ -- Expect -- ------------ function Get_Command_Output (Command : access GNAT.Expect.Process_Descriptor'Class) return String; -- Runs Command until it finishes, and return its output. -- This automatically closes the process cleanly. ------------------ -- File systems -- ------------------ function Executable_Location return String; -- Returns the name of the parent directory where the executable is stored -- (so if you are running "prefix/my_exe", you would get "prefix/"). -- A special case is done for "bin" directories, which are consumed -- (so if you are running "prefix/bin/my_exe", you would get "prefix/"). -- The returned directory always ends with a directory separator. function Executable_Path return String; -- Returns absolute path to the current executable. -- -- On Linux, Windows and MacOS the procedure is safe and will return always -- the right executable. For other platforms the function might return -- an incorrect value if environment is modified (executable parameters, -- current directory and/or PATH variable). function Is_Directory_Separator (C : Character) return Boolean; pragma Inline (Is_Directory_Separator); -- Returns True if C is a directory separator function Join_Path (Path : String; Path1, Path2, Path3, Path4 : String := "") return String; -- Join one or more path into a single one. Note that if one argument is an -- absolute path then previous arguments will be ignored. procedure Add_Search_Path (Variable : String; Path : String); -- Prepend a path to an environment variable containing a list of paths. -- If Path is already in the search list, subsequent occurrences will be -- removed and thus limit final path value size. ----------- -- Dates -- ----------- No_Time : Ada.Calendar.Time renames GNAT.Calendar.No_Time; function Time_Value (Str : String) return Ada.Calendar.Time; -- Checks the validity of Str as a string representing a date -- using the same formats as in GNAT.Calendar.Time_IO.Value. In addition, -- it also supports timezones (as output for instance by PostgreSQL) -- 1970-01-01 12:00:00+01 -- and the ISO format -- 1970-01-01T12:00:00+01 or 1970-01-01T12:00:00Z -- All the above can start with the day spelled out, as in "thu, " -- -- The input date is assumed to be in UTC unless a timezone is specified -- as hours with a final "[+-]\d\d", or as hours and minutes with -- "[+-]\d\d\d\d" or "[+-]\d\d:\d\d" -- -- The output date is always returned for the UTC time zone. -- So if you are in GMT+12 and you parse "2017-01-01T11:00:00", the -- result date will be: year=2016, month=12, day=31, time=23:00:00. -- If you want to spit the resulting time to extract the components, -- you should use: -- Ada.Calendar.Formatting.Split (.., Time_Zone => 0); function UTC_Time_Offset (Local : Ada.Calendar.Time) return Duration is (Duration (Ada.Calendar.Time_Zones.UTC_Time_Offset (Local)) * 60); -- Returns the difference between the implementation-defined time zone of -- Calendar, and UTC time, at the time Local. If the time zone of the -- Calendar implementation is unknown, raises Unknown_Zone_Error. function Truncate (Date : Time; Time_Zone : Time_Zones.Time_Offset := 0) return Time; -- Removes time part from the date in specified timezone. -- For example, if we want to truncate "2015 May 10 05:00 GMT+6" time at -- UTC timezone we are going to get "2015 May 9, 00:00 UTC" because -- "2015 May 10 05:00 GMT+6" equal to "2015 May 9 23:00 UTC". private Empty_Array : constant Unbounded_String_Array (1 .. 0) := (others => Ada.Strings.Unbounded.Null_Unbounded_String); end GNATCOLL.Utils; gnatcoll-core-21.0.0/src/gnatcoll-email-utils.adb0000644000175000017500000017117213661715457021502 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ pragma Ada_2012; with Ada.Calendar; use Ada.Calendar; with Ada.Calendar.Time_Zones; use Ada.Calendar.Time_Zones; with Ada.Calendar.Formatting; use Ada.Calendar.Formatting; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Streams; with Ada.Strings.Hash_Case_Insensitive; with GNATCOLL.Coders.Base64; use GNATCOLL.Coders; with GNATCOLL.Utils; use GNATCOLL.Utils; with System.WCh_Con; use System.WCh_Con; with GNAT.Decode_String; pragma Warnings (Off); -- Ada.Strings.Unbounded.Aux is an internal GNAT unit with Ada.Strings.Unbounded.Aux; pragma Warnings (On); package body GNATCOLL.Email.Utils is U_Charset_US_ASCII : constant Unbounded_String := To_Unbounded_String (Charset_US_ASCII); package Decode_Shift_JIS is new GNAT.Decode_String (WCEM_Shift_JIS); package Decode_EUC is new GNAT.Decode_String (WCEM_EUC); package Decode_UTF8 is new GNAT.Decode_String (WCEM_UTF8); type Next_Char_Acc is access procedure (S : String; Index : in out Natural); -- Procedure moving Index from one character to the next in S, -- taking multi-byte encodings into account. procedure Single_Byte_Next_Char (S : String; Index : in out Natural); -- Default version for single-byte charsets, simply incrementing Index --------------------------- -- Single_Byte_Next_Char -- --------------------------- procedure Single_Byte_Next_Char (S : String; Index : in out Natural) is pragma Unreferenced (S); begin Index := Index + 1; end Single_Byte_Next_Char; function Next_Char_For_Charset (Charset : String) return Next_Char_Acc is (if Charset = Charset_Shift_JIS then Decode_Shift_JIS.Next_Wide_Character'Access elsif Charset = Charset_EUC then Decode_EUC.Next_Wide_Character'Access elsif Charset = Charset_UTF_8 then Decode_UTF8.Next_Wide_Character'Access else Single_Byte_Next_Char'Access); -- Next_Char procedure for the named Charset procedure Next_Char_Ignore_Invalid (NC : Next_Char_Acc; S : String; Index : in out Natural); pragma Inline (Next_Char_Ignore_Invalid); -- Call NC (S, Index), but if an exception is raised (e.g. due to -- an invalid encoding in S, fall back to incrementing Index by 1. ------------------------------ -- Next_Char_Ignore_Invalid -- ------------------------------ procedure Next_Char_Ignore_Invalid (NC : Next_Char_Acc; S : String; Index : in out Natural) is Orig_Index : constant Natural := Index; begin NC (S, Index); exception when others => Index := Orig_Index + 1; end Next_Char_Ignore_Invalid; function Needs_Quoting (Char : Character; Where : Region; Is_EOL : Boolean) return Boolean; -- Return True if C needs to be quoted when appearing in Region, False -- otherwise. Is_EOL indicates whether Char is last of its line. function Needs_Quoting (U : Unbounded_String; Where : Region; Is_EOL : Boolean) return Boolean; -- True if any non-whitespace character in U needs to be quoted per -- the above function. Is_EOL indicates whehter the last character of U is -- last of its line. procedure Read_Integer (S : String; Index : in out Integer; Value : out Integer); -- return the integer starting at Index, and moves Index after the integer procedure Skip_Comment (S : String; Index : in out Integer); -- Skip the comment, if any, that starts at Index. -- In RFC 2822, comments are between parenthesis, and can be nested procedure Skip_Quoted_String (S : String; Index : in out Integer); -- Skip a quoted string, taking properly into account the backslashes -- Index should point after the opening quote. procedure Parse_And_Skip_Address (From_C : in out Charset_String_List.Cursor; From : in out Integer; Address : out Email_Address); -- Parse the first email address at (From_C, From), and leaves them after -- it, so that if there are more addresses in From_C they can all be parsed -- easily. procedure Parse_And_Skip_Address (Str : String; From : in out Integer; Buffer : in out Unbounded_String; Buffer_Has_At : in out Boolean; In_Quote : in out Boolean; Comment : in out Unbounded_String; Address : in out Email_Address; Found : out Boolean); -- Internal version of Parse_And_Skip_Address, which applies to a -- us-ascii string. It maintains internal data. -- In_Quote indicates whether we are initially within an open quote ("), -- and on exit whether we are still processing a quoted string. procedure Post_Process_Address (Address : in out Email_Address; Buffer, Comment : Unbounded_String; Buffer_Has_At : Boolean); -- Complete the data in Address, given Buffer and Comment that were -- generated by Parse_And_Skip_Address. This procedure should be called -- after Parse_And_Skip_Address, before returning an address to the user. Special_Chars : constant array (Character) of Boolean := ('[' | ']' | '\' | '(' | ')' | '<' | '>' | '@' | ',' => True, ':' | ';' | '"' | '.' => True, others => False); Quoted_Chars : constant array (Character) of Boolean := ('[' | ']' | '\' | '(' | ')' | '"' => True, others => False); Qp_Convert : constant array (Character) of Short_Integer := ('0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15, 'a' => 10, 'b' => 11, 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15, others => -1); Hex_Chars : constant array (0 .. 15) of Character := "0123456789ABCDEF"; ------------------------ -- Skip_Quoted_String -- ------------------------ procedure Skip_Quoted_String (S : String; Index : in out Integer) is begin while Index <= S'Last loop if S (Index) = '"' then Index := Index + 1; return; elsif S (Index) = '\' then Index := Index + 2; else Index := Index + 1; end if; end loop; -- There is no closing '"' Index := S'Last + 1; end Skip_Quoted_String; ------------------ -- Skip_Comment -- ------------------ procedure Skip_Comment (S : String; Index : in out Integer) is Par : Natural := 1; begin if S (Index) = '(' then Index := Index + 1; while Index <= S'Last loop if S (Index) = ')' then Par := Par - 1; if Par = 0 then Index := Index + 1; return; else Index := Index + 1; end if; elsif S (Index) = '(' then Par := Par + 1; Index := Index + 1; elsif S (Index) = '\' then Index := Index + 2; else Index := Index + 1; end if; end loop; -- No closing ')' Index := S'Last + 1; end if; end Skip_Comment; ------------------ -- Read_Integer -- ------------------ procedure Read_Integer (S : String; Index : in out Integer; Value : out Integer) is Start : constant Integer := Index; begin if S (Index) = '-' or else S (Index) = '+' then Index := Index + 1; end if; while Index <= S'Last and then S (Index) in '0' .. '9' loop Index := Index + 1; end loop; Value := Integer'Value (S (Start .. Index - 1)); end Read_Integer; ------------- -- To_Time -- ------------- function To_Time (Date : String; Format : Time_Format := Time_RFC2822) return Ada.Calendar.Time is Index : Integer := Date'First; Index2 : Integer; Year : Year_Number := Year_Number'First; Month : Month_Number := Month_Number'First; Day : Day_Number := Day_Number'First; Seconds : Day_Duration := 0.0; TZ : Time_Offset := 0; Time_Error : exception; procedure Read_Day; procedure Read_Month; procedure Read_Year; procedure Read_Time; -- Read the day of month or the year procedure Read_Time_Zone; procedure Read_Day is begin Read_Integer (Date (Index .. Date'Last), Index, Value => Index2); Day := Day_Number (Index2); Skip_Whitespaces (Date (Index .. Date'Last), Index); end Read_Day; procedure Read_Month is pragma Warnings (Off); type Month_Name is (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec); pragma Warnings (On); begin Month := Month_Name'Pos (Month_Name'Value (Date (Index .. Index + 2))) + 1; -- Some mailers print the month in full, not just the first three -- chars. Although this isn't part of the RFC 2822, we still want to -- handle these Index := Index + 3; while Index <= Date'Last and then not Is_Whitespace (Date (Index)) loop Index := Index + 1; end loop; Skip_Whitespaces (Date (Index .. Date'Last), Index); exception when others => -- This really means the date format is incorrect! null; end Read_Month; procedure Read_Year is begin Read_Integer (Date (Index .. Date'Last), Index, Value => Index2); if Index2 < 0 then raise Time_Error; elsif Index2 <= 49 then Year := Year_Number (2000 + Index2); elsif Index2 <= 99 then Year := Year_Number (1900 + Index2); else Year := Year_Number (Index2); end if; Skip_Whitespaces (Date (Index .. Date'Last), Index); end Read_Year; procedure Read_Time is begin Read_Integer (Date (Index .. Date'Last), Index, Value => Index2); Seconds := Seconds + Day_Duration (Index2) * 3600.0; if Date (Index) /= ':' then raise Time_Error; end if; Index := Index + 1; Read_Integer (Date (Index .. Date'Last), Index, Value => Index2); Seconds := Seconds + Day_Duration (Index2) * 60.0; if Date (Index) = ':' then Index := Index + 1; Read_Integer (Date (Index .. Date'Last), Index, Value => Index2); Seconds := Seconds + Day_Duration (Index2); end if; Skip_Whitespaces (Date (Index .. Date'Last), Index); end Read_Time; procedure Read_Time_Zone is TZ_Local : Integer; type Named_TZ is (AST, ADT, EST, EDT, CST, CDT, MST, MDT, PST, PDT); Named_TZ_Offset : constant array (Named_TZ) of Time_Offset := (AST => -240, ADT => -180, EST => -300, EDT => -240, CST => -360, CDT => -300, MST => -420, MDT => -360, PST => -480, PDT => -420); begin -- Timezone (we might have none in badly formed dates) if Index < Date'Last then if Date (Index) = '-' or else Date (Index) = '+' or else Date (Index) in '0' .. '9' then Read_Integer (Date (Index .. Date'Last), Index, Value => TZ_Local); TZ := Time_Offset ((TZ_Local / 100) * 60 + TZ_Local mod 100); else -- The timezone table does not include the military time zones -- defined in RFC822, other than Z. According to RFC1123, the -- description in RFC822 gets the signs wrong, so we can't rely -- on any such time zones. RFC1123 recommends that numeric -- timezone indicators be used instead of timezone names. if Date (Index .. Index + 1) = "UT" or else Date (Index .. Index + 2) = "UTC" or else Date (Index) = 'Z' then TZ := 0; else TZ := Named_TZ_Offset (Named_TZ'Value (Date (Index .. Index + 2))); end if; end if; end if; exception when Constraint_Error => -- Invalid time zone, just ignore null; end Read_Time_Zone; begin -- RFC 2822 format is -- [day-of-week] ","] day month-name year FWS time-of-day FWS tz -- year := 4 * DIGIT | 2 * DIGIT -- day := 1*2DIGIT -- time-of-day := hour ":" minute [":" second] -- tz := (("+" | "-") 4DIGIT) | "UT" | "GMT" | ... -- -- Envelope format is -- Tue Jan 24 14:48:49 2006 +0100 Skip_Whitespaces (Date (Index .. Date'Last), Index); -- Day of week is optional, skip it case Format is when Time_RFC2822 => Index2 := Next_Occurrence (Date (Index .. Date'Last), ','); if Index2 <= Date'Last then Index := Index2 + 1; Skip_Whitespaces (Date (Index .. Date'Last), Index); end if; Read_Day; Read_Month; Read_Year; Read_Time; when Time_Envelope => Index := Index + 3; Skip_Whitespaces (Date (Index .. Date'Last), Index); Read_Month; Read_Day; Read_Time; Read_Year; end case; Read_Time_Zone; return Time_Of (Year, Month, Day, Seconds, Time_Zone => TZ); exception when Time_Error => return No_Time; when Constraint_Error => return No_Time; end To_Time; ----------------- -- Format_Time -- ----------------- function Format_Time (Date : Ada.Calendar.Time) return String is Result : Unbounded_String; Y : Year_Number; M : Month_Number; D : Day_Number; H : Hour_Number; Min : Minute_Number; S : Second_Number; SS : Second_Duration; begin Split (Date, Y, M, D, H, Min, S, SS, Time_Zone => 0); Result := To_Unbounded_String (Image (Integer (H), Min_Width => 2) & ":"); Append (Result, Image (Integer (Min), Min_Width => 2) & ":"); Append (Result, Image (Integer (S), Min_Width => 2)); return To_String (Result); end Format_Time; ----------------- -- Format_Date -- ----------------- Day_Names : constant array (Day_Name) of String (1 .. 3) := (Monday => "Mon", Tuesday => "Tue", Wednesday => "Wed", Thursday => "Thu", Friday => "Fri", Saturday => "Sat", Sunday => "Sun"); Month_Names : constant array (1 .. 12) of String (1 .. 3) := (1 => "Jan", 2 => "Feb", 3 => "Mar", 4 => "Apr", 5 => "May", 6 => "Jun", 7 => "Jul", 8 => "Aug", 9 => "Sep", 10 => "Oct", 11 => "Nov", 12 => "Dec"); function Format_Date (Date : Ada.Calendar.Time; Use_GMT : Boolean := False; From_Line : Boolean := False; No_TZ : Boolean := False; Show_Time : Boolean := True; Show_Seconds : Boolean := True; Show_Day : Boolean := True) return String is Result : Unbounded_String; Y : Year_Number; M : Month_Number; D : Day_Number; H : Hour_Number; Min : Minute_Number; S : Second_Number; SS : Second_Duration; TZ : Time_Offset := 0; RFC_TZ : Integer; Unknown_TZ : Boolean := False; begin if not (Use_GMT or else No_TZ) then begin -- Number of minutes difference for the timezone TZ := UTC_Time_Offset (Date); exception when Unknown_Zone_Error => Unknown_TZ := True; end; end if; -- We cannot use GNAT.Calendar.Time_IO for week days, since we always -- want the english names, not the locale's version. Split (Date, Y, M, D, H, Min, S, SS, Time_Zone => TZ); if Show_Day then -- Note: we can't just call Day_Of_Week (Date), since this gives the -- day of week for Date *in the local time zone*, and in No_TZ or -- Use_GMT mode we want the day of week for Date -- *in the UT time zone*. So, we conjure up another date whose year, -- month, and day number in month (and therefore day of week) in -- local time are the same as those of Date in GMT (namely, Y, M, -- and D). Result := To_Unbounded_String (Day_Names (Day_Of_Week (Ada.Calendar.Time_Of (Y, M, D)))); if From_Line then Append (Result, " "); else Append (Result, ", "); end if; end if; if not From_Line then Append (Result, Image (Integer (D), Min_Width => 2) & " "); end if; Append (Result, Month_Names (M) & ' '); if From_Line then Append (Result, Image (Integer (D), Min_Width => 2) & " "); else Append (Result, Image (Integer (Y), Min_Width => 2) & " "); end if; if Show_Time then Append (Result, Image (Integer (H), Min_Width => 2) & ":"); Append (Result, Image (Integer (Min), Min_Width => 2)); if Show_Seconds then Append (Result, ":" & Image (Integer (S), Min_Width => 2)); end if; end if; if From_Line then Append (Result, " " & Image (Integer (Y), Min_Width => 2)); end if; if not No_TZ then if Use_GMT then Append (Result, " GMT"); elsif Unknown_TZ then Append (Result, " -0000"); else RFC_TZ := Integer ((TZ / 60) * 100 + TZ mod 60); Append (Result, " " & Image (RFC_TZ, Min_Width => 4, Force_Sign => True)); end if; end if; return To_String (Result); end Format_Date; ------------------- -- Parse_Address -- ------------------- function Parse_Address (Email : String) return Email_Address is Index : Integer := Email'First; Result : Email_Address; Buffer : Unbounded_String; Buffer_Has_At : Boolean := False; Comment : Unbounded_String; Found : Boolean; In_Quote : Boolean := False; begin Parse_And_Skip_Address (Str => Email, From => Index, Buffer => Buffer, Buffer_Has_At => Buffer_Has_At, In_Quote => In_Quote, Comment => Comment, Address => Result, Found => Found); Post_Process_Address (Address => Result, Buffer => Buffer, Comment => Comment, Buffer_Has_At => Buffer_Has_At); return Result; end Parse_Address; ---------------------------- -- Parse_And_Skip_Address -- ---------------------------- procedure Parse_And_Skip_Address (Str : String; From : in out Integer; Buffer : in out Unbounded_String; Buffer_Has_At : in out Boolean; In_Quote : in out Boolean; Comment : in out Unbounded_String; Address : in out Email_Address; Found : out Boolean) is Index : Integer; begin if In_Quote then Index := From; Skip_Quoted_String (Str (Index .. Str'Last), Index); Address.Real_Name := Trim (To_Unbounded_String (Str (From + 1 .. Index - 2)), Ada.Strings.Both); From := Index + 1; In_Quote := False; end if; -- Skip spaces while From <= Str'Last and then (Str (From) = ASCII.LF or else Str (From) = ASCII.CR or else Str (From) = ASCII.HT or else Str (From) = ' ') loop From := From + 1; end loop; -- Only parse the contents of us-ascii strings. The rest cannot -- contain email addresses nor comments anyway. while From <= Str'Last loop if From <= Str'Last then if Str (From) = '(' then -- A comment. Ignored in general, but if we do not have a -- real name, it is likely to be contained in this -- comment, which is what some old mailers used to do: -- report@gnat.com (Report) Index := From; Skip_Comment (Str (From .. Str'Last), Index); Append (Comment, Str (From + 1 .. Index - 2)); From := Index; elsif Str (From) = '<' then -- The email address Index := From; while Index <= Str'Last and then Str (Index) /= '>' loop Index := Index + 1; end loop; Address.Address := To_Unbounded_String (Str (From + 1 .. Index - 1)); From := Index + 1; -- ',' is the standard separator in mail messages, but ';' is -- often used by users when manually typing a list of addresses elsif Str (From) = ',' or else Str (From) = ';' or else Str (From) = ASCII.LF or else Str (From) = ASCII.CR or else Str (From) = ASCII.HT or else (Buffer_Has_At and then Str (From) = ' ') then -- End of current address From := From + 1; Found := True; return; elsif Str (From) = '"' then Index := From + 1; Skip_Quoted_String (Str (Index .. Str'Last), Index); if Index > Str'Last then In_Quote := True; end if; Address.Real_Name := Trim (To_Unbounded_String (Str (From + 1 .. Index - 2)), Ada.Strings.Both); if Index <= Str'Last and then Str (Index) = ' ' then From := Index + 1; else From := Index; end if; else if Str (From) = '@' then Buffer_Has_At := True; end if; Append (Buffer, Str (From)); From := From + 1; end if; end if; end loop; Found := False; end Parse_And_Skip_Address; ---------------------------- -- Parse_And_Skip_Address -- ---------------------------- procedure Parse_And_Skip_Address (From_C : in out Charset_String_List.Cursor; From : in out Integer; Address : out Email_Address) is use Charset_String_List; Buffer : Unbounded_String; Comment : Unbounded_String; Buffer_Has_At : Boolean := False; In_Quote : Boolean := False; -- Quotes are not necessarily encoded, and we could have for instance: -- " =?iso-2022-jp?b?...?= " -- which is made of several strings: one for the opening quote, one for -- the encoded name, and a last one that includes the quote and the -- email address. Continue : Boolean; procedure Analyze (CS : Charset_String); -- Analyze a given element of the list. Done in nested procedure to -- avoid a copy of each element of Email procedure Analyze (CS : Charset_String) is Tmp : Unbounded_String; Found : Boolean := False; begin -- Only parse the contents of us-ascii strings. The rest cannot -- contain email addresses nor comments anyway if CS.Charset = Charset_US_ASCII then Parse_And_Skip_Address (Str => To_String (CS.Contents), From => From, Buffer => Buffer, Buffer_Has_At => Buffer_Has_At, In_Quote => In_Quote, Comment => Comment, Address => Address, Found => Found); else -- Reencode, to preserve names in international charsets Encode (Str => To_String (CS.Contents), Charset => To_String (CS.Charset), Where => Addr_Header, Result => Tmp); Append (Buffer, Tmp); end if; Continue := not Found; end Analyze; -- Start of processing for Parse_And_Skip_Address begin Address := Null_Address; while Has_Element (From_C) loop Query_Element (From_C, Analyze'Unrestricted_Access); exit when not Continue; Next (From_C); From := 1; end loop; Post_Process_Address (Address, Buffer, Comment, Buffer_Has_At); end Parse_And_Skip_Address; -------------------------- -- Post_Process_Address -- -------------------------- procedure Post_Process_Address (Address : in out Email_Address; Buffer, Comment : Unbounded_String; Buffer_Has_At : Boolean) is pragma Unreferenced (Buffer_Has_At); begin if Address.Address = Null_Unbounded_String then -- Ideally, we should test whether Buffer contains a @ string. But -- there are degenerate cases where we have an email address on its -- own with no @ sign, and we want to handle them for backward -- compatibility... Address.Address := Trim (Buffer, Ada.Strings.Both); else if Address.Real_Name = Null_Unbounded_String then if Buffer = Null_Unbounded_String then Address.Real_Name := Trim (Comment, Ada.Strings.Both); else Address.Real_Name := Trim (Buffer, Ada.Strings.Both); end if; end if; end if; end Post_Process_Address; ---------------- -- To_Address -- ---------------- function To_Address (Address : String; Real_Name : String := "") return Email_Address is begin return (Address => To_Unbounded_String (Address), Real_Name => To_Unbounded_String (Real_Name)); end To_Address; ------------------- -- Get_Addresses -- ------------------- function Get_Addresses (Str : String) return Address_Set.Set is use Charset_String_List; L : Charset_String_List.List; begin Append (L, (Contents => To_Unbounded_String (Str), Charset => To_Unbounded_String (Charset_US_ASCII))); return Get_Addresses (L); end Get_Addresses; function Get_Addresses (Str : Charset_String_List.List) return Address_Set.Set is use Charset_String_List, Address_Set; C : Charset_String_List.Cursor := First (Str); From : Integer := 1; Result : Address_Set.Set; Addr : Email_Address; begin while Has_Element (C) loop Parse_And_Skip_Address (C, From, Addr); if Addr /= Null_Address then Include (Result, Addr); end if; end loop; return Result; end Get_Addresses; --------------- -- To_String -- --------------- function To_String (Addresses : Address_Set.Set; Separator : String := ", "; Address_Only : Boolean := False; Charset : String := Charset_US_ASCII) return String is use Address_Set; Tmp : Unbounded_String; C : Address_Set.Cursor := First (Addresses); begin while Has_Element (C) loop if Tmp /= Null_Unbounded_String then Append (Tmp, Separator); end if; if Address_Only then Append (Tmp, Element (C).Address); else Append (Tmp, Format_Address (Element (C), Charset)); end if; Next (C); end loop; return To_String (Tmp); end To_String; -------------------- -- Format_Address -- -------------------- function Format_Address (Email : Email_Address; Charset : String := Charset_US_ASCII) return Charset_String_List.List is L : Charset_String_List.List; begin -- If Charset is US-ASCII, we can't rely on RFC 2047 encoding to -- protect any special characters, so fall back to legacy formatting -- routine, which will do backslash-escaping as needed. If nothing -- needs quoting, don't bother to go trough RFC 2047 either. if Charset = Charset_US_ASCII or else not Needs_Quoting (Email.Real_Name, Is_EOL => False, Where => Addr_Header) then L.Append ((Contents => To_Unbounded_String (Legacy_Format_Address (Real => To_String (Email.Real_Name), Address => To_String (Email.Address))), Charset => U_Charset_US_ASCII)); -- Case where we have a non-ASCII charset specified else -- Here we have a non-default Charset specified: RFC 2047 encoding -- will also take care of escaping special characters. L.Append ((Contents => Email.Real_Name, Charset => To_Unbounded_String (Charset))); -- Actual address must not be encoded in any way: add a separate -- US ASCII section. L.Append (Charset_String' (Contents => " <" & Email.Address & ">", Charset => U_Charset_US_ASCII)); end if; return L; end Format_Address; -------------------- -- Format_Address -- -------------------- function Format_Address (Email : Email_Address; Charset : String := Charset_US_ASCII) return Unbounded_String is Res : Unbounded_String; begin To_String (Format_Address (Email, Charset), Res); return Res; end Format_Address; --------------------------- -- Legacy_Format_Address -- --------------------------- function Legacy_Format_Address (Real : String; Address : String) return String is Has_Special : Boolean := False; -- True if Real contains any special character that needs to be -- escaped in an RFC 2822 address header. begin if Real = "" then return Address; else for C in Real'Range loop if Special_Chars (Real (C)) then Has_Special := True; exit; end if; end loop; if Has_Special then return '"' & Quote (Real) & """ <" & Address & '>'; else return Quote (Real) & " <" & Address & '>'; end if; end if; end Legacy_Format_Address; ----------- -- Quote -- ----------- function Quote (Str : String) return String is Result : String (Str'First .. Str'Last * 2); Index : Integer := Result'First; begin for C in Str'Range loop if Quoted_Chars (Str (C)) then Result (Index) := '\'; Index := Index + 1; end if; Result (Index) := Str (C); Index := Index + 1; end loop; return Result (Result'First .. Index - 1); end Quote; ------------- -- Unquote -- ------------- function Unquote (Str : String) return String is Result : String (Str'Range); Index : Integer := Result'First; C : Integer := Str'First; begin while C <= Str'Last loop if Str (C) = '\' and then C < Str'Last then Result (Index) := Str (C + 1); C := C + 1; else Result (Index) := Str (C); end if; C := C + 1; Index := Index + 1; end loop; return Result (Result'First .. Index - 1); end Unquote; ---------- -- Hash -- ---------- function Hash (Addr : Email_Address) return Ada.Containers.Hash_Type is begin return Ada.Strings.Hash_Case_Insensitive (To_String (Addr.Address)); end Hash; -------------------- -- Get_Recipients -- -------------------- function Get_Recipients (Msg : Message'Class; Include_From : Boolean := False) return Address_Set.Set is use Address_Set; Iter : Header_Iterator; H : Header; Result : Address_Set.Set; begin Iter := Get_Headers (Msg); loop Next (Iter, H => H); exit when H = Null_Header; if Get_Name (H) = "to" or else Get_Name (H) = "cc" or else Get_Name (H) = "resent-to" or else Get_Name (H) = "resent-cc" or else (Include_From and then Get_Name (H) = "from") then -- ??? Should avoid extra copy here Union (Result, Get_Recipients (H)); end if; end loop; return Result; end Get_Recipients; -------------------- -- Get_Recipients -- -------------------- function Get_Recipients (H : Header'Class) return Address_Set.Set is begin if H.Contents = null then return Address_Set.Empty_Set; else return Get_Addresses (H.Contents.Value); end if; end Get_Recipients; ------------- -- Flatten -- ------------- procedure Flatten (List : Charset_String_List.List; Result : out Unbounded_String) is use Charset_String_List; C : Charset_String_List.Cursor := First (List); begin Result := Null_Unbounded_String; while Has_Element (C) loop Append (Result, Element (C).Contents); Next (C); end loop; end Flatten; --------------- -- To_String -- --------------- procedure To_String (List : Charset_String_List.List; Result : out Unbounded_String; Where : Any_Header := Other_Header) is use Charset_String_List; C : Charset_String_List.Cursor := First (List); Tmp : Unbounded_String; begin Result := Null_Unbounded_String; while Has_Element (C) loop Encode (Str => To_String (Element (C).Contents), Charset => To_String (Element (C).Charset), Where => Where, Result => Tmp); Append (Result, Tmp); Next (C); end loop; end To_String; ------------------------- -- Domain_From_Address -- ------------------------- function Domain_From_Address (Email : String) return String is begin for E in Email'First .. Email'Last - 1 loop if Email (E) = '@' then return Email (E + 1 .. Email'Last); end if; end loop; return ""; end Domain_From_Address; function Domain_From_Address (Email : Email_Address) return String is begin return Domain_From_Address (To_String (Email.Address)); end Domain_From_Address; ----------------------- -- Login_From_Address -- ------------------------ function Login_From_Address (Email : String) return String is begin for E in Email'First .. Email'Last loop if Email (E) = '@' then return Email (Email'First .. E - 1); end if; end loop; return Email; end Login_From_Address; function Login_From_Address (Email : Email_Address) return String is begin return Login_From_Address (To_String (Email.Address)); end Login_From_Address; ------------------- -- Needs_Quoting -- ------------------- function Needs_Quoting (Char : Character; Where : Region; Is_EOL : Boolean) return Boolean is begin if Char = ' ' or else Char = ASCII.HT then return Is_EOL or else Where in Any_Header; elsif Char = '=' or else Char = '?' or else Character'Pos (Char) not in 32 .. 126 then return True; else return Where = Addr_Header and then Special_Chars (Char); end if; end Needs_Quoting; function Needs_Quoting (U : Unbounded_String; Where : Region; Is_EOL : Boolean) return Boolean is use Ada.Strings.Unbounded.Aux; Str : Big_String_Access; Last : Integer; EOL : Boolean; begin Get_String (U, Str, Last); for J in Str'First .. Last loop EOL := Is_EOL and then J = Last; -- No need to quote whitespace unless at EOL if (Str (J) = ' ' or else Str (J) = ASCII.HT) and then not EOL then null; elsif Needs_Quoting (Str (J), Where, EOL) then return True; end if; end loop; return False; end Needs_Quoting; ----------------------------- -- Quoted_Printable_Encode -- ----------------------------- procedure Quoted_Printable_Encode (Str : String; Charset : String; Max_Block_Len : Integer := Integer'Last; Where : Region := Text; Result : out Unbounded_String) is Block_Prefix : constant String := (if Where in Any_Header then "=?" & Charset & "?q?" else ""); Block_Suffix : constant String := (if Where in Any_Header then "?=" else ""); Block_Separator : constant String := (if Where in Any_Header then " " else "=" & ASCII.LF); -- In Text, use a soft line break Current_Len : Natural := 0; Max : constant Natural := Integer'Min (Max_Block_Len, (if Where in Any_Header then 75 else 76)) - Block_Prefix'Length - Block_Suffix'Length - (Block_Separator'Length - 1); -- Note: Block_Separator may produce a printable character, so must be -- counted against the limit. function Quote (S : String) return String; -- Encode all characters in S procedure Append (Substring : String; Splittable : Boolean); -- Append Substring to Result, taking into account the max line length. -- If Splittable is false, Substring cannot be cut ----------- -- Quote -- ----------- function Quote (S : String) return String is P : Integer; Result : String (1 .. 3 * S'Length); Last : Integer := 0; begin for J in S'Range loop if S (J) = ' ' and then Where in Any_Header then Last := Last + 1; Result (Last) := '_'; else Last := Last + 3; P := Character'Pos (S (J)); Result (Last - 2 .. Last) := ('=', Hex_Chars (P / 16), Hex_Chars (P mod 16)); end if; end loop; return Result (1 .. Last); end Quote; ------------ -- Append -- ------------ procedure Append (Substring : String; Splittable : Boolean) is S : Integer := Substring'First; begin if Substring'Length = 0 then return; end if; if Splittable then while Substring'Last - S + 1 > Max - Current_Len loop if Current_Len = 0 then Append (Result, Block_Prefix); end if; Append (Result, Substring (S .. S + Max - Current_Len - 1)); Append (Result, Block_Suffix & Block_Separator); S := S + Max - Current_Len; Current_Len := 0; -- We just started a new line end loop; if Current_Len = 0 then Append (Result, Block_Prefix); end if; Append (Result, Substring (S .. Substring'Last)); Current_Len := Current_Len + Substring'Last - S + 1; else if Current_Len + Substring'Length > Max then if Current_Len /= 0 then Append (Result, Block_Suffix & Block_Separator); end if; Current_Len := 0; Append (Result, Block_Prefix); Append (Result, Substring); Current_Len := Substring'Length; else if Current_Len = 0 then Append (Result, Block_Prefix); end if; Append (Result, Substring); Current_Len := Current_Len + Substring'Length; end if; end if; end Append; Start, Next, Last : Integer; -- Start of current encoded sequence, -- start of next encoded sequence, -- last element of previous encoded sequence. procedure Passthrough; -- Output previous span of unencoded characters, i.e. -- from Last + 1 to Start - 1. ----------------- -- Passthrough -- ----------------- procedure Passthrough is begin Append (Str (Last + 1 .. Start - 1), Splittable => True); end Passthrough; Next_Char : constant Next_Char_Acc := (if Where in Any_Header then Next_Char_For_Charset (Charset) else Single_Byte_Next_Char'Access); -- Start of processing for Quoted_Printable_Encode begin Result := Null_Unbounded_String; Next := Str'First; Last := Next - 1; loop Start := Next; exit when Start > Str'Last; -- Find end of possibly multibyte sequence starting at Start Next := Start; Next_Char_Ignore_Invalid (Next_Char, Str, Next); -- We encode single characters if needed, and always encode -- all multibyte characters. if Last > Start + 1 or else Needs_Quoting (Str (Start), Where, Is_EOL => Start = Str'Last) then Passthrough; Last := Next - 1; Append (Quote (Str (Start .. Last)), Splittable => False); end if; end loop; Passthrough; if Current_Len /= 0 then Append (Result, Block_Suffix); end if; end Quoted_Printable_Encode; ----------------------------- -- Quoted_Printable_Decode -- ----------------------------- procedure Quoted_Printable_Decode (Str : String; Result : out Unbounded_String; Where : Region := Text) is Start : Integer := -1; S : Integer; function Is_Hex (Char : Character) return Boolean; -- Return true if Char is an hexa character ------------ -- Is_Hex -- ------------ function Is_Hex (Char : Character) return Boolean is begin return Qp_Convert (Char) >= 0; end Is_Hex; -- Start of processing for Quoted_Printable_Decode begin S := Str'First; Result := Null_Unbounded_String; while S <= Str'Last loop if Str (S) = '_' and then Where in Any_Header then -- Encoded SPACE if Start /= -1 then Append (Result, Str (Start .. S - 1)); Start := -1; end if; Append (Result, ' '); elsif Str (S) /= '=' then -- Regular character if Start = -1 then Start := S; end if; elsif Str (S) = '=' and then S + 1 <= Str'Last and then Str (S + 1) = ASCII.LF then -- Soft line break if Start /= -1 then Append (Result, Str (Start .. S - 1)); Start := -1; end if; S := S + 1; elsif S + 2 <= Str'Last and then Is_Hex (Str (S + 1)) and then Is_Hex (Str (S + 2)) then -- Valid quote sequence if Start /= -1 then Append (Result, Str (Start .. S - 1)); Start := -1; end if; Append (Result, Character'Val (Qp_Convert (Str (S + 1)) * 16 + Qp_Convert (Str (S + 2)))); S := S + 2; else -- Invalid quote sequence. Leave it as is if Start /= -1 then Append (Result, Str (Start .. S - 1)); Start := -1; end if; end if; S := S + 1; end loop; if Start /= -1 then Append (Result, Str (Start .. Str'Last)); end if; end Quoted_Printable_Decode; ------------------- -- Base64_Encode -- ------------------- procedure Base64_Encode (Str : String; Charset : String; Max_Block_Len : Integer := Integer'Last; Where : Region := Text; Result : out Unbounded_String) is procedure Put_Parts (Part : String); procedure Put_Parts (Part : String) is begin Append (Result, Part); end Put_Parts; begin Result := Null_Unbounded_String; Base64_Encode (Str, Charset, Max_Block_Len, Where, Put_Parts'Access); end Base64_Encode; procedure Base64_Encode (Str : String; Charset : String; Max_Block_Len : Integer := Integer'Last; Where : Region := Text; Put_Parts : not null access procedure (Part : String)) is use Ada.Streams; Block_Prefix : constant String := "=?" & Charset & "?b?"; Block_Suffix : constant String := "?="; Block_Separator : constant String := " "; In_Bytes : Stream_Element_Array (Stream_Element_Offset (Str'First) .. Stream_Element_Offset (Str'Last)) with Import; for In_Bytes'Address use Str'Address; Max : constant Natural := Integer'Max (1, (Integer'Min (Max_Block_Len, 75) - Block_Prefix'Length - Block_Suffix'Length) / 4) * 3; -- Length of the original data producing output of Max_Block_Len. -- Divide by 4 and multiply by 3 because base64 encoder takes 3 -- original bytes (i.e. 24 bits) and produces 4 6-bit-coded characters. -- Another effect of length adjustment by 3/4 is that most of the blocks -- in headers are not going to be aligned by '=' character. -- Note: block separator does not contain any printable character, so -- does not count against the limit. Coder : Base64.Encoder_Type; procedure Encode_Append (First, Last : Stream_Element_Offset; Last_One : Boolean); -- Encode Str and append result to output, splitting if necessary. -- If Where is Any_Header, then never split Str across two different -- blocks. ------------------- -- Encode_Append -- ------------------- procedure Encode_Append (First, Last : Stream_Element_Offset; Last_One : Boolean) is In_Last : Stream_Element_Offset; Out_Last : Stream_Element_Offset; Out_Chars : String (1 .. (Str'Length + 2) * 4 / 3); Out_Bytes : Stream_Element_Array (1 .. Out_Chars'Length) with Import; for Out_Bytes'Address use Out_Chars'Address; begin Put_Parts (Block_Prefix); Coder.Initialize; Coder.Transcode (In_Bytes (First .. Last), In_Last, Out_Bytes, Out_Last, Finish); Coder.Close; Put_Parts (Out_Chars (1 .. Natural (Out_Last)) & Block_Suffix & (if Last_One then "" else Block_Separator)); end Encode_Append; Start, Next, Fit : Integer; -- Start of current encoded sequence, -- start of next encoded sequence, -- last element of previous encoded sequence. Index : Stream_Element_Offset := In_Bytes'First; Next_Char : constant Next_Char_Acc := Next_Char_For_Charset (Charset); -- In message bodies, multi-byte encodings can be -- split across multiple lines; in headers, they can't -- be split across multiple encoded words. -- Start of processing for Base64_Encode begin Fit := Str'First; if Where in Any_Header then while Fit <= Str'Last loop Start := Fit; Next := Fit; while Next - Start <= Max loop Fit := Next; exit when Fit > Str'Last; Next_Char_Ignore_Invalid (Next_Char, Str, Next); end loop; Encode_Append (First => Stream_Element_Offset (Start), Last => Stream_Element_Offset (Fit - 1), Last_One => Fit > Str'Last); end loop; else Coder.Initialize; loop declare Out_Last : Stream_Element_Offset; Text : String (1 .. Integer'Min (Max_Block_Len, 76)); Buffer : Stream_Element_Array (1 .. Text'Length) with Import; for Buffer'Address use Text'Address; Flush : constant Flush_Mode := (if Index > In_Bytes'Last - Text'Length * 3 / 4 + 1 then Finish else No_Flush); First : constant Boolean := Index = In_Bytes'First; begin Coder.Transcode (In_Bytes (Index .. In_Bytes'Last), Index, Buffer, Out_Last, Flush => Flush); if 1 <= Out_Last then Put_Parts ((if First then "" else (1 => ASCII.LF)) & Text (1 .. Natural (Out_Last))); end if; exit when Flush = Finish; Index := Index + 1; end; end loop; Coder.Close; end if; end Base64_Encode; ------------------- -- Base64_Decode -- ------------------- procedure Base64_Decode (Str : String; Result : out Unbounded_String) is use Ada.Streams; Decoder : Base64.Decoder_Type; Src : Stream_Element_Array (1 .. Str'Length) with Import; for Src'Address use Str'Address; Index : Stream_Element_Offset := Src'First; Dest : Stream_Element_Array (1 .. 4096); Last : Stream_Element_Offset; Text : String (1 .. Dest'Length); for Text'Address use Dest'Address; Flush : Flush_Mode := No_Flush; begin Decoder.Initialize; Result := Null_Unbounded_String; while Index <= Src'Last loop if Index > Src'Last - Dest'Length then Flush := Finish; end if; Decoder.Transcode (In_Data => Src (Index .. Src'Last), In_Last => Index, Out_Data => Dest, Out_Last => Last, Flush => Flush); Append (Result, Text (1 .. Integer (Last))); exit when Flush = Finish; Index := Index + 1; end loop; Decoder.Close; end Base64_Decode; ------------ -- Encode -- ------------ procedure Encode (Str : String; Charset : String := Charset_US_ASCII; Where : Region := Text; Result : out Unbounded_String) is Encoding : Encoding_Type; Set : constant String := To_Lower (Charset); begin -- Preferred encoding are the same as in Python if Set = Charset_US_ASCII then Encoding := Encoding_7bit; elsif Set = Charset_ISO_8859_1 or else Set = "latin_1" or else Set = "latin-1" or else Set = Charset_ISO_8859_2 or else Set = "latin_2" or else Set = "latin-2" or else Set = Charset_ISO_8859_3 or else Set = "latin_3" or else Set = "latin-3" or else Set = Charset_ISO_8859_4 or else Set = "latin_4" or else Set = "latin-4" or else Set = Charset_ISO_8859_9 or else Set = "latin_5" or else Set = "latin-5" or else Set = Charset_ISO_8859_10 or else Set = "latin_6" or else Set = "latin-6" or else Set = Charset_ISO_8859_13 or else Set = "latin_7" or else Set = "latin-7" or else Set = Charset_ISO_8859_14 or else Set = "latin_8" or else Set = "latin-8" or else Set = Charset_ISO_8859_15 or else Set = "latin_9" or else Set = "latin-9" or else Set = Charset_Windows_1252 or else Set = "viscii" or else Set = Charset_UTF_8 or else Set = "utf8" then Encoding := Encoding_QP; else Encoding := Encoding_Base64; end if; case Encoding is when Encoding_Base64 => Base64_Encode (Str, Charset => Set, Where => Where, Result => Result); when Encoding_QP => Quoted_Printable_Encode (Str, Charset => Set, Where => Where, Result => Result); when others => Result := To_Unbounded_String (Str); end case; end Encode; ------------------- -- Decode_Header -- ------------------- procedure Decode_Header (Str : String; Default_Charset : String := Charset_US_ASCII; Result : out Charset_String_List.List; Where : Any_Header := Other_Header) is use Charset_String_List; Start : Integer; Index : Integer; Index2 : Integer; Section : Charset_String; Encoding : Encoding_Type; S : Integer; procedure Append (Section : Charset_String); -- Add Section to the result, merging with previous section if needed. -- If Section.Charset is empty, use Default_Charset, or Charset_US_ASCII -- if possible. ------------ -- Append -- ------------ procedure Append (Section : Charset_String) is NSection : Charset_String := Section; begin if NSection.Charset = Null_Unbounded_String then declare Raw_Str : Ada.Strings.Unbounded.Aux.Big_String_Access; Raw_Last : Integer; begin Ada.Strings.Unbounded.Aux.Get_String (NSection.Contents, Raw_Str, Raw_Last); for J in Raw_Str'First .. Raw_Last loop if Character'Pos (Raw_Str (J)) not in 32 .. 126 then NSection.Charset := To_Unbounded_String (Default_Charset); exit; end if; end loop; if NSection.Charset = Null_Unbounded_String then NSection.Charset := U_Charset_US_ASCII; end if; end; end if; -- Now append the new section to the sequence if Is_Empty (Result) then Append (Result, NSection); else -- An empty section between two encoded ones must be ignored if NSection.Charset /= Default_Charset and then Element (Last (Result)).Charset = Default_Charset then declare Previous : constant Unbounded_String := Element (Last (Result)).Contents; begin if Index_Non_Blank (Previous) < 1 then Delete_Last (Result); end if; end; end if; -- Try to merge Section with previous one, if possible if not Is_Empty (Result) and then NSection.Charset = Element (Last (Result)).Charset then Replace_Element (Result, Last (Result), (Contents => Element (Last (Result)).Contents & NSection.Contents, Charset => NSection.Charset)); else Append (Result, NSection); end if; end if; end Append; -- Start of processing for Decode_Header begin Result := Charset_String_List.Empty_List; S := Str'First; Start := Str'First; while S < Str'Last loop if Str (S) = '=' and then S < Str'Last and then Str (S + 1) = '?' then Index := Next_Occurrence (Str (S + 2 .. Str'Last), '?'); if Index < Str'Last then Section.Charset := To_Unbounded_String (To_Lower (Str (S + 2 .. Index - 1))); case To_Lower (Str (Index + 1)) is when 'q' => Encoding := Encoding_QP; when 'b' => Encoding := Encoding_Base64; when others => Encoding := Encoding_7bit; end case; if Encoding /= Encoding_7bit and then Index + 2 < Str'Last then if Str (Index + 2) = '?' then -- So far we have the prefix =??? Index2 := Index + 3; Index := Next_Occurrence (Str (Index2 .. Str'Last), '?'); if Index < Str'Last and then Str (Index + 1) = '=' then case Encoding is when Encoding_QP => Quoted_Printable_Decode (Str (Index2 .. Index - 1), Where => Where, Result => Section.Contents); when Encoding_Base64 => Base64_Decode (Str (Index2 .. Index - 1), Result => Section.Contents); when others => null; end case; -- Deal with non-encoded-word part: charset is -- set to Default_Charset, unless the string has -- no character which need to be encoded, in which -- case use US-ASCII instead. if Start <= S - 1 then declare Raw_Section : String renames Str (Start .. S - 1); -- Part of Str that is not an encoded-word begin Append ((Contents => To_Unbounded_String (Raw_Section), Charset => Null_Unbounded_String)); end; end if; Append (Section); S := Index + 2; Start := S; else S := Index2; end if; else S := Index + 1; end if; else S := Index + 1; end if; end if; end if; S := S + 1; end loop; if Start <= Str'Last then Append ((Contents => To_Unbounded_String (Str (Start .. Str'Last)), Charset => Null_Unbounded_String)); end if; end Decode_Header; ------------------- -- Get_Main_Type -- ------------------- function Get_Main_Type (MIME_Type : String) return String is begin for M in MIME_Type'Range loop if MIME_Type (M) = '/' then return MIME_Type (MIME_Type'First .. M - 1); end if; end loop; return MIME_Type; end Get_Main_Type; ------------------ -- Get_Sub_Type -- ------------------ function Get_Sub_Type (MIME_Type : String) return String is begin for M in MIME_Type'Range loop if MIME_Type (M) = '/' then if M + 1 <= MIME_Type'Last then return MIME_Type (M + 1 .. MIME_Type'Last); else return ""; end if; end if; end loop; return MIME_Type; end Get_Sub_Type; end GNATCOLL.Email.Utils; gnatcoll-core-21.0.0/src/gnatcoll-coders-base64.ads0000644000175000017500000001347713661715457021642 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides Base64 encoding/decoding with Interfaces; package GNATCOLL.Coders.Base64 is type Base64_Mode is (MIME, URL); -- Base64 encoding variants for encoding routines, -- RFC4648 -- MIME - section 4 -- URL - section 5 type Coder_Type is abstract new Coder_Interface with private; -- Common code and data for base64 encoder/decoder overriding function Total_In (Coder : Coder_Type) return Stream_Element_Count; -- Returns total amount of input data sent into the coder type Encoder_Type is new Coder_Type with private; -- Base64 encoder procedure Initialize (Coder : in out Encoder_Type; Wrap : Natural := 0; Mode : Base64_Mode := MIME); -- Initialize base64 encoder. -- Wrap defines line length in encoded output. -- Mode MIME mean base64 encoding defined in RFC 2045 section 6.8. -- Mode URL mean base64 encoding defined in RFC 4648 section 4. overriding procedure Transcode (Coder : in out Encoder_Type; In_Data : Stream_Element_Array; In_Last : out Stream_Element_Offset; Out_Data : out Stream_Element_Array; Out_Last : out Stream_Element_Offset; Flush : Flush_Mode) with Pre => In_Data'First > Stream_Element_Offset'First and then Out_Data'First > Stream_Element_Offset'First; -- Encode data to base64 from In_Data to Out_Date. -- In_Last is the index of last element from In_Data accepted by -- the Coder. -- Out_Last is the index of the last element written to the Out_Data. overriding function Is_Open (Coder : Encoder_Type) return Boolean; -- Indicates that encoder is ready for data processing overriding function Total_Out (Coder : Encoder_Type) return Stream_Element_Count; -- Returns total amount of output data taken from the coder overriding function Finished (Coder : Encoder_Type) return Boolean; -- Indicates that incoming data stream finished and all internally -- processed data is out of coder. overriding procedure Close (Coder : in out Encoder_Type); -- Close encoding type Decoder_Type is new Coder_Type with private; -- Base64 decoder procedure Initialize (Coder : in out Decoder_Type); -- Initialize base64 decoder overriding procedure Transcode (Coder : in out Decoder_Type; In_Data : Stream_Element_Array; In_Last : out Stream_Element_Offset; Out_Data : out Stream_Element_Array; Out_Last : out Stream_Element_Offset; Flush : Flush_Mode) with Pre => In_Data'First > Stream_Element_Offset'First and then Out_Data'First > Stream_Element_Offset'First; -- Decode base64 data from In_Data to Out_Date. overriding function Is_Open (Coder : Decoder_Type) return Boolean; -- Indicates that decoder is ready for data processing overriding function Total_Out (Coder : Decoder_Type) return Stream_Element_Count; -- Returns total amount of output data taken from the decoder overriding function Finished (Coder : Decoder_Type) return Boolean; -- Indicates that incoming data stream finished and all internally -- processed data is out of decoder. overriding procedure Close (Coder : in out Decoder_Type); -- Close decoding private use Interfaces; type Base64_Encode_Array is array (Unsigned_8 range 0 .. 63) of Character; type Coder_Type is abstract new Coder_Interface with record In_Count : Stream_Element_Count := 0; Out_Count : Stream_Element_Count := 0; Finish : Boolean := False; end record; type Encoder_Type is new Coder_Type with record To_Char : access constant Base64_Encode_Array; Lines : Stream_Element_Count := 1; -- Number of lines Left : Unsigned_16 := 0; Left_Bits : Integer := 0; Align : Boolean := False; Wrap : Natural; end record; type Decoder_Type is new Coder_Type with record Bits : Unsigned_8 := 0; Has : Boolean := False; Open : Boolean := False; end record; end GNATCOLL.Coders.Base64; gnatcoll-core-21.0.0/src/gnatcoll.ads0000644000175000017500000000337613661715457017300 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2005-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package GNATCOLL is pragma Pure; end GNATCOLL; gnatcoll-core-21.0.0/src/gnatcoll-io-native-codec__win32.adb0000644000175000017500000000663213661715457023402 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- Windows version: all paths are already utf-8 encoded by mingw when -- GNAT_CODE_PAGE is CP_UTF8. Otherwise we need to convert to/from UTF-8. with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Environment_Variables; use Ada.Environment_Variables; with GNAT.Decode_UTF8_String; use GNAT.Decode_UTF8_String; with GNAT.Encode_UTF8_String; use GNAT.Encode_UTF8_String; separate (GNATCOLL.IO.Native) package body Codec is Is_UTF8 : Boolean := True; procedure Initialize; -- Initialize current Windows code page ---------------- -- Initialize -- ---------------- procedure Initialize is begin if Exists ("GNAT_CODE_PAGE") and then Value ("GNAT_CODE_PAGE") = "CP_ACP" then Is_UTF8 := False; end if; end Initialize; ------------- -- To_UTF8 -- ------------- function To_UTF8 (Path : Wide_String) return String is begin if Is_UTF8 then return To_String (Path); else return Encode_Wide_String (Path); end if; end To_UTF8; function To_UTF8 (Path : FS_String) return String is begin if Is_UTF8 then return String (Path); else return Encode_Wide_String (To_Wide_String (String (Path))); end if; end To_UTF8; --------------- -- From_UTF8 -- --------------- function From_UTF8 (Path : String) return Wide_String is begin if Is_UTF8 then return Decode_Wide_String (Path); else return To_Wide_String (Path); end if; end From_UTF8; function From_UTF8 (Path : String) return FS_String is begin return FS_String (To_String (From_UTF8 (Path))); end From_UTF8; begin Initialize; end Codec; gnatcoll-core-21.0.0/src/update_path.c0000644000175000017500000000350413661715457017437 0ustar nicolasnicolas/*---------------------------------------------------------------------------- -- G N A T C O L L -- -- -- -- Copyright (C) 2002-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -----------------------------------------------------------------------------*/ /* Dummy version of update_path (needed by osint.adb) */ char * update_path (char *path, char *key) { return path; } gnatcoll-core-21.0.0/src/gnatcoll-templates.adb0000644000175000017500000002100413661715457021237 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2008-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with GNAT.Strings; use GNAT.Strings; package body GNATCOLL.Templates is procedure Find_Identifier (Str : String; Delimiter : Character; First : in out Integer; Last : out Integer; First_After : out Integer); -- Set Last to the last character of the identifier name. -- First should point to the first candidate character, but could be -- moved forward if it points to a curly brace. ---------- -- Free -- ---------- procedure Free (Substrings : in out Substitution_Array) is begin for S in Substrings'Range loop Free (Substrings (S).Name); Free (Substrings (S).Value); end loop; end Free; --------------------- -- Find_Identifier -- --------------------- procedure Find_Identifier (Str : String; Delimiter : Character; First : in out Integer; Last : out Integer; First_After : out Integer) is begin if Str (First) = Delimiter then -- We are escaping the delimiter by doubling it Last := First; First_After := First + 1; elsif Str (First) = '{' then First := First + 1; Last := First; while Last <= Str'Last and then Str (Last) /= '}' loop Last := Last + 1; end loop; First_After := Last + 1; Last := Last - 1; elsif Str (First) = '(' then First := First + 1; Last := First; while Last <= Str'Last and then Str (Last) /= ')' loop Last := Last + 1; end loop; First_After := Last + 1; Last := Last - 1; elsif Is_Digit (Str (First)) then Last := First + 1; while Last <= Str'Last and then Is_Digit (Str (Last)) loop Last := Last + 1; end loop; if Last <= Str'Last and then Str (Last) = '-' then Last := Last + 1; end if; First_After := Last; Last := Last - 1; elsif Is_Alphanumeric (Str (First)) then Last := First + 1; while Last <= Str'Last and then (Is_Alphanumeric (Str (Last)) or else Str (Last) = '_') loop Last := Last + 1; end loop; First_After := Last; Last := Last - 1; else Last := First; First_After := Last + 1; end if; end Find_Identifier; ---------------- -- Substitute -- ---------------- function Substitute (Str : String; Substrings : Substitution_Array := No_Substitution; Callback : Substitute_Callback := null; Delimiter : Character := Default_Delimiter; Recursive : Boolean := False; Errors : Error_Handling := Keep_As_Is) return String is Result : Unbounded_String; First, Last : Natural := Str'First; Found : Boolean; Identifier_First, Identifier_Last, First_After : Natural; Quoted : Boolean := False; begin while First <= Str'Last loop Last := First; -- Skip constant substrings while Last <= Str'Last and then Str (Last) /= Delimiter loop if Str (Last) = '"' then Quoted := not Quoted; end if; Last := Last + 1; end loop; if Last = Str'Last then Last := Last + 1; end if; Append (Result, Str (First .. Last - 1)); exit when Last > Str'Last; -- Find name of identifier First := Last + 1; Identifier_First := First; Find_Identifier (Str, Delimiter, Identifier_First, Last, First_After); -- Does the identifier contain a default value? Identifier_Last := Last; for D in Identifier_First .. Identifier_Last - 1 loop if Str (D) = ':' and then Str (D + 1) = '-' then Identifier_Last := D - 1; exit; end if; end loop; Found := False; for S in Substrings'Range loop if Substrings (S).Name.all = Str (Identifier_First .. Identifier_Last) then if Recursive then Append (Result, Substitute (Str => Substrings (S).Value.all, Substrings => Substrings, Callback => Callback, Delimiter => Delimiter, Recursive => Recursive)); else Append (Result, Substrings (S).Value.all); end if; Found := True; exit; end if; end loop; -- When doubled, the delimiter is always replaced with itself by -- default. if not Found and then Identifier_Last = Identifier_First and then Str (Identifier_First) = Delimiter then -- We are escaping the Substitution_Char by doubling it Append (Result, Delimiter); Found := True; elsif not Found and then Callback /= null then begin declare Sub : constant String := Callback (Str (Identifier_First .. Identifier_Last), Quoted); begin if Recursive then Append (Result, Substitute (Str => Sub, Substrings => Substrings, Callback => Callback, Delimiter => Delimiter, Recursive => Recursive)); else Append (Result, Sub); end if; Found := True; end; exception when Invalid_Substitution => Found := False; end; end if; -- If still not found, try the default value if it was specified if not Found and then Identifier_Last < Last then Append (Result, Str (Identifier_Last + 3 .. Last)); Found := True; end if; if not Found then case Errors is when Keep_As_Is => Append (Result, Str (First - 1 .. First_After - 1)); when Replace_With_Empty => null; when Report_Error => raise Invalid_Substitution; end case; end if; First := First_After; end loop; return To_String (Result); end Substitute; end GNATCOLL.Templates; gnatcoll-core-21.0.0/src/gnatcoll-strings.ads0000644000175000017500000000417613661715457020766 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- Optimized strings, with local buffer for performance and optional -- copy-on-write. -- See details in gnatcoll-strings_impl.ads with GNATCOLL.Strings_Impl; with Ada.Characters.Handling; use Ada.Characters.Handling; package GNATCOLL.Strings is new GNATCOLL.Strings_Impl.Strings (SSize => GNATCOLL.Strings_Impl.Optimal_String_Size, Character_Type => Character, Character_String => String); gnatcoll-core-21.0.0/src/gnatcoll-projects-aux.ads0000644000175000017500000000743013661715457021715 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2013-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides additional services when using Project Files. -- These services are of two kinds: -- - functions to get data types from the underlying implementation -- of the Project Manager. -- - subprograms that are usually only used by specialized tools. with GPR; use GPR; package GNATCOLL.Projects.Aux is function To_Project_Id (Project : Projects.Project_Type) return GPR.Project_Id; pragma Inline (To_Project_Id); -- Give access to the Project_Id of a project function Project_Tree_Ref_Of (Project : Projects.Project_Type) return GPR.Project_Tree_Ref; pragma Inline (Project_Tree_Ref_Of); -- Give access to the Project_Tree_Ref of a project function Create_Ada_Mapping_File (Project : Projects.Project_Type) return String; -- Creates a temporary file that contains the mapping of the Ada units -- in the project tree rooted at the project Project and returns the full -- path of the temporary file. If the creation of this mapping file is -- unsuccessful, either an exception is raised or the empty string is -- returned. -- -- It is the responsibility of the user to delete this temporary file when -- it is no longer needed, either directly or by calling -- Delete_All_Temp_Files. function Create_Config_Pragmas_File (Project : Projects.Project_Type) return String; -- Creates a temporary file that contains the configuration pragmas for -- the project tree rooted at Project and returns the full path of the -- temporary file. If the creation of this mapping file is unsuccessful, -- either an exception is raised or the empty string is returned. -- -- It is the responsibility of the user to delete this temporary file when -- it is no longer needed, either directly or by calling -- Delete_All_Temp_Files. procedure Delete_All_Temp_Files (Root_Project : Projects.Project_Type); -- Delete all the temporary files that have been created by the Project -- Manager in the project tree rooted at Root_Project. end GNATCOLL.Projects.Aux; gnatcoll-core-21.0.0/src/link_max.c0000644000175000017500000000345113661715457016744 0ustar nicolasnicolas/*---------------------------------------------------------------------------- -- G N A T C O L L -- -- -- -- Copyright (C) 2007-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -----------------------------------------------------------------------------*/ /* Dummy version of __gnat_link_max (needed by mlib-utl.adb) */ int __gnat_link_max = 8192; gnatcoll-core-21.0.0/src/gnatcoll-opt_parse.ads0000644000175000017500000004237013661715457021267 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Finalization; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with GNATCOLL.Strings; use GNATCOLL.Strings; private with Ada.Containers.Vectors; private with GNATCOLL.Refcount; private with GNATCOLL.Locks; package GNATCOLL.Opt_Parse is -- WARNING: The interface of this package is still unstable. No guarantees -- of API stability. USE AT YOUR OWN RISK. -- -- This package is meant to create powerful command line argument parsers -- in a declarative fashion. The generated argument parsers have a typed -- interface, in that, you can specify the types of expected arguments and -- options, and get a statically typed API to access the results. -- -- Here is a small example of how to create a command line argument parser -- and how to use it: -- -- with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; -- with Ada.Text_IO; use Ada.Text_IO; -- with GNATCOLL.Opt_Parse; use GNATCOLL.Opt_Parse; -- -- .. code:: ada -- -- procedure Main is -- -- package Arg is -- Parser : Argument_Parser := Create_Argument_Parser -- (Help => "Help string for the parser"); -- -- package Files is new Parse_Positional_Arg_List -- (Parser => Parser, -- Name => "files", -- Arg_Type => Unbounded_String, -- Help => "The files to parse"); -- -- package Quiet is new Parse_Flag -- (Parser => Parser, -- Short => "-q", -- Long => "--quiet", -- Help => "Whether the tool should be quiet or not"); -- -- package Charset is new Parse_Option -- (Parser => Parser, -- Short => "-C", -- Long => "--charset", -- Arg_Type => Unbounded_String, -- Help => -- "What charset to use for the analysis context. " -- & "Default is ""latin-1""", -- Default_Val => To_Unbounded_String ("latin-1")); -- end Arg; -- -- begin -- -- if Arg.Parser.Parse then -- -- Put_Line ("Charset = " & To_String (Arg.Charset.Get)); -- for F of Arg.Files.Get loop -- if not Arg.Quiet.Get then -- Put_Line ("Got file " & To_String (F)); -- end if; -- end loop; -- end if; -- -- end Main; -- -- All generic packages for argument parsers accept an ``Enabled`` formal, -- set to True by default. When set to False, it cancels the registration -- of the argument parser. In this case, depending on the argument -- specifics, calling its ``Get`` primitive may return a default value or -- raise a ``Disabled_Error`` exception. This feature is useful to disable -- one or several options depending on some compile-time configuration -- without using complex declarations blocks nested in ``if`` statements. ------------------------ -- General API types -- ------------------------ type Argument_Parser is tagged limited private; -- Base type for the Opt_Parse API. Represents a general parser to which -- you will associate specific argument parsers. type Parsed_Arguments is private; -- Type containing the result of an argument parse. Please note you do -- not need to handle the return value if you don't want to, in which case -- you will be able to access argument values directly via the generic Get -- functions. No_Arguments : constant XString_Array (1 .. 0) := (others => <>); -- Constant for the absence of command line arguments No_Parsed_Arguments : constant Parsed_Arguments; -- Constant for a null Parsed_Arguments value function Parse (Self : in out Argument_Parser; Arguments : XString_Array := No_Arguments) return Boolean; -- Parse the command line arguments for Self. function Parse (Self : in out Argument_Parser; Arguments : XString_Array := No_Arguments; Result : out Parsed_Arguments) return Boolean; -- Parse command line arguments for Self. Return arguments explicitly. function Create_Argument_Parser (Help : String; Command_Name : String := "") return Argument_Parser; -- Create an argument parser with the provided help string. function Help (Self : Argument_Parser) return String; -- Return the help for this parser as a String. -------------------------- -- Conversion functions -- -------------------------- -- Convenience conversion functions that are meant to be used in -- instantiations. function Convert (Arg : String) return XString renames To_XString; function Convert (Arg : String) return Unbounded_String renames To_Unbounded_String; function Convert (Arg : String) return Integer; Opt_Parse_Error : exception; -- Exception signaling an error in the parser. This is the error that you -- will get in the rare cases where you do something invalid with a Parser -- (such as querying results without calling parse first), and this is -- also the exception that you should raise in conversion functions when -- receiving an invalid value. Disabled_Error : exception; -- Exception raised when trying to get the value of a disabled argument -- parser that is not a list and provides no default value. -------------------------------- -- Specific argument parsers -- -------------------------------- generic Parser : in out Argument_Parser; -- Argument_Parser owning this argument. Name : String; -- Name of the argument in the parser. Used mainly to formal the help -- output. Help : String; -- Help string for the argument. Allow_Empty : Boolean := False; -- Whether empty lists are allowed or not. type Arg_Type is private; -- Type of the elements contained in the list. with function Convert (Arg : String) return Arg_Type is <>; -- Conversion function to convert from a raw string argument to the -- argument type. Enabled : Boolean := True; -- Whether to add this argument parser. Note that if it is disabled and -- Allow_Empty is False, Get will raise a Disabled_Error. package Parse_Positional_Arg_List is type Result_Array is array (Positive range <>) of Arg_Type; No_Results : constant Result_Array (1 .. 0) := (others => <>); function Get (Args : Parsed_Arguments := No_Parsed_Arguments) return Result_Array; end Parse_Positional_Arg_List; -- Parse a list of positional arguments. This parser can only be the last -- positional parser, since it will parse every remaining argument on the -- command line. generic Parser : in out Argument_Parser; -- Argument_Parser owning this argument. Name : String; -- Name of the argument in the parser. Used mainly to formal the help -- output. Help : String := ""; -- Help string for the argument. type Arg_Type is private; -- Type of the positional argument. with function Convert (Arg : String) return Arg_Type is <>; -- Conversion function to convert from a raw string argument to the -- argument type. Enabled : Boolean := True; -- Whether to add this argument parser. Note that if it is disabled, Get -- will raise a Disabled_Error. package Parse_Positional_Arg is function Get (Args : Parsed_Arguments := No_Parsed_Arguments) return Arg_Type; end Parse_Positional_Arg; -- Parse a positional argument. A positional argument is any argument. If -- the conversion fails, then it will make the whole argument parser fail. generic Parser : in out Argument_Parser; -- Argument_Parser owning this argument. Short : String := ""; -- Short form for this flag. Should start with one dash and be followed -- by one or two alphanumeric characters. Long : String; -- Long form for this flag. Should start with two dashes. Help : String := ""; -- Help string for the argument. Enabled : Boolean := True; -- Whether to add this argument parser package Parse_Flag is function Get (Args : Parsed_Arguments := No_Parsed_Arguments) return Boolean; end Parse_Flag; -- Parse a Flag option. A flag takes no other argument, and its result is a -- boolean: False if the flag is not passed, True otherwise. generic Parser : in out Argument_Parser; -- Argument_Parser owning this argument. Short : String := ""; -- Short form for this flag. Should start with one dash and be followed -- by one or two alphanumeric characters. Long : String; -- Long form for this flag. Should start with two dashes. Help : String := ""; -- Help string for the argument. type Arg_Type is private; -- Type of the option. with function Convert (Arg : String) return Arg_Type is <>; -- Conversion function to convert from a raw string argument to the -- argument type. Default_Val : Arg_Type; -- Default value if the option is not passed. Enabled : Boolean := True; -- Whether to add this argument parser package Parse_Option is function Get (Args : Parsed_Arguments := No_Parsed_Arguments) return Arg_Type; end Parse_Option; -- Parse a regular option. A regular option is of the form "--option val", -- or "--option=val", or "-O val", or "-Oval". If option is not passed, -- takes the default value. generic Parser : in out Argument_Parser; -- Argument_Parser owning this argument. Short : String := ""; -- Short form for this flag. Should start with one dash and be followed -- by one or two alphanumeric characters. Long : String; -- Long form for this flag. Should start with two dashes. Help : String := ""; -- Help string for the argument. Accumulate : Boolean := False; -- If True, then this argument can be passed several times and behaves -- each time as a regular option, only with results accumulated in the -- result list. If False, user needs to pass a list of values after the -- flag name. type Arg_Type is private; -- Type of the option list. with function Convert (Arg : String) return Arg_Type is <>; -- Conversion function to convert from a raw string argument to the -- argument type. Enabled : Boolean := True; -- Whether to add this argument parser package Parse_Option_List is type Result_Array is array (Positive range <>) of Arg_Type; No_Results : constant Result_Array (1 .. 0) := (others => <>); function Get (Args : Parsed_Arguments := No_Parsed_Arguments) return Result_Array; end Parse_Option_List; -- Parse an option list. A regular option is of the form -- "--option val, val2, val3", or "-O val val2 val3". -- -- Values cannot start with - or --. -- -- If Accumulate is True, mix between option and option list. Parses like -- regular option, which you can parse several time, and put results in a -- list. private use GNATCOLL.Locks; type Argument_Parser_Data; type Argument_Parser_Data_Access is access all Argument_Parser_Data; package XString_Vectors is new Ada.Containers.Vectors (Positive, XString); type Parser_Type is abstract tagged record Name : XString; -- Name of the parser Help : XString; -- Help string for the parser Position : Positive; -- Position of this parser in its enclosing Arguments_Parser Opt : Boolean := True; -- Whether this parser is optional or not Parser : Argument_Parser_Data_Access; end record; subtype Parser_Return is Integer range -1 .. Integer'Last; -- Return value of a Parser. Represents a position, except for the special -- value Error_Return. Error_Return : constant Parser_Return := 0; -- Special value for Parser_Return when there was an error function Parse_Args (Self : in out Parser_Type; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return is abstract; -- Return the result of parsing arguments for this parser. Abstract method -- that must be overloaded by implementations. function Parse (Self : in out Parser_Type'Class; Args : XString_Array; Pos : Positive; Result : in out Parsed_Arguments) return Parser_Return; -- Return the result of parsing arguments for this parser. Function wrapper -- around `Parse_Args` that is called by Arguments_Parser. function Usage (Self : Parser_Type) return String is abstract; -- Return a usage string for this parser. Abstract method that must be -- overloaded. function Help_Name (Self : Parser_Type) return String is (To_String (Self.Name)); -- Return the help name for this parser. function Does_Accumulate (Self : Parser_Type) return Boolean is (False); -- Whether this parser accumulates results or not. If it does, then it is -- valid to call Parse on it several time, which will add to results. type Parser_Access is access all Parser_Type'Class; package Parsers_Vectors is new Ada.Containers.Vectors (Positive, Parser_Access); subtype Parser_Vector is Parsers_Vectors.Vector; type Argument_Parser_Data is record Help, Command_Name : XString; Positional_Args_Parsers, Opts_Parsers : Parser_Vector; All_Parsers : Parser_Vector; Default_Result : Parsed_Arguments := No_Parsed_Arguments; Help_Flag : Parser_Access := null; Mutex : aliased Mutual_Exclusion; -- Mutex used to make Get_Result thread safe end record; type Parser_Result is abstract tagged record Start_Pos, End_Pos : Positive; end record; procedure Release (Result : in out Parser_Result) is abstract; -- Derived types must override this to clean-up internal data when the -- Parser_Result object is about to be deallocated. type Parser_Result_Access is access all Parser_Result'Class; function Get_Result (Self : Parser_Type'Class; Args : Parsed_Arguments) return Parser_Result_Access; function Has_Result (Self : Parser_Type'Class; Args : Parsed_Arguments) return Boolean; type Parser_Result_Array is array (Positive range <>) of Parser_Result_Access; type Parser_Result_Array_Access is access all Parser_Result_Array; type XString_Array_Access is access all XString_Array; type Parsed_Arguments_Type is record Raw_Args : XString_Array_Access; Results : Parser_Result_Array_Access; end record; procedure Release (Self : in out Parsed_Arguments_Type); package Parsed_Arguments_Shared_Ptrs is new GNATCOLL.Refcount.Shared_Pointers (Parsed_Arguments_Type, Release => Release, Atomic_Counters => True); type Parsed_Arguments is record Ref : Parsed_Arguments_Shared_Ptrs.Ref := Parsed_Arguments_Shared_Ptrs.Null_Ref; end record; No_Parsed_Arguments : constant Parsed_Arguments := (Ref => Parsed_Arguments_Shared_Ptrs.Null_Ref); type Argument_Parser is new Ada.Finalization.Limited_Controlled with record Data : Argument_Parser_Data_Access := null; end record; overriding procedure Finalize (Self : in out Argument_Parser); end GNATCOLL.Opt_Parse; gnatcoll-core-21.0.0/src/gnatcoll-io-remote-unix.ads0000644000175000017500000001165513661715457022156 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNAT.Strings; use GNAT.Strings; with GNATCOLL.Remote; use GNATCOLL.Remote; package GNATCOLL.IO.Remote.Unix is -- The following methods are equivalent to their native counterparts. -- See GNATCOLL.IO for documentation. function Current_Dir (Exec : access Server_Record'Class) return FS_String; function Home_Dir (Exec : access Server_Record'Class) return FS_String; function Tmp_Dir (Exec : access Server_Record'Class) return FS_String; function Get_Logical_Drives (Exec : access Server_Record'Class) return String_List_Access; function Locate_On_Path (Exec : access Server_Record'Class; Base : FS_String) return FS_String; function Is_Regular_File (Exec : access Server_Record'Class; File : FS_String) return Boolean; function Size (Exec : access Server_Record'Class; File : FS_String) return Long_Integer; function Is_Directory (Exec : access Server_Record'Class; File : FS_String) return Boolean; function Is_Symbolic_Link (Exec : access Server_Record'Class; File : FS_String) return Boolean; function File_Time_Stamp (Exec : access Server_Record'Class; File : FS_String) return Ada.Calendar.Time; function Is_Writable (Exec : access Server_Record'Class; File : FS_String) return Boolean; procedure Set_Writable (Exec : access Server_Record'Class; File : FS_String; State : Boolean); function Is_Readable (Exec : access Server_Record'Class; File : FS_String) return Boolean; procedure Set_Readable (Exec : access Server_Record'Class; File : FS_String; State : Boolean); procedure Rename (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean); procedure Copy (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean); procedure Delete (Exec : access Server_Record'Class; File : FS_String; Success : out Boolean); function Read_Whole_File (Exec : access Server_Record'Class; File : FS_String) return GNAT.Strings.String_Access; function Read_Whole_File (Exec : access Server_Record'Class; File : FS_String) return GNATCOLL.Strings.XString; function Write_File (Exec : access Server_Record'Class; File : FS_String; Content : String) return Boolean; function Change_Dir (Exec : access Server_Record'Class; Dir : FS_String) return Boolean; function Read_Dir (Exec : access Server_Record'Class; Dir : FS_String; Dirs_Only : Boolean := False; Files_Only : Boolean := False) return GNAT.Strings.String_List; function Make_Dir (Exec : access Server_Record'Class; Dir : FS_String; Recursive : Boolean) return Boolean; procedure Copy_Dir (Exec : access Server_Record'Class; From : FS_String; Dest : FS_String; Success : out Boolean); procedure Delete_Dir (Exec : access Server_Record'Class; Dir : FS_String; Recursive : Boolean; Success : out Boolean); end GNATCOLL.IO.Remote.Unix; gnatcoll-core-21.0.0/src/gnatcoll-arg_lists.adb0000644000175000017500000004206713661715457021244 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2009-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with GNAT.OS_Lib; use GNAT.OS_Lib; with Ada.Unchecked_Deallocation; with Ada.Containers; use Ada.Containers; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Strings.Maps; use Ada.Strings.Maps; with GNATCOLL.Scripts.Utils; use GNATCOLL.Scripts.Utils; package body GNATCOLL.Arg_Lists is procedure Parse_Command_Line_String (CL : in out Arg_List; Text : String) with Pre => CL.Mode = Separate_Args; -- Factor code between variants of Parse_String. -- This processes Text as if it were passed on a command line (for instance -- the bash command line) and adds the arguments to CL. function Escape_Backslashes (A : Unbounded_String) return Unbounded_String; -- Escape backslashes in A ------------------------ -- Escape_Backslashes -- ------------------------ function Escape_Backslashes (A : Unbounded_String) return Unbounded_String is S : constant String := To_String (A); R : Unbounded_String; begin for J in S'Range loop case S (J) is when '\' => Append (R, "\\"); when others => Append (R, S (J)); end case; end loop; return R; end Escape_Backslashes; ------------------------------- -- Parse_Command_Line_String -- ------------------------------- procedure Parse_Command_Line_String (CL : in out Arg_List; Text : String) is function Process (A : String) return Argument_Type with Pre => A'Length > 0; -- Post-process on each argument returned by Argument_String_To_List. -- Note that Argument_String_To_List_With_Triple_Quotes never returns -- empty arguments. ------------- -- Process -- ------------- function Process (A : String) return Argument_Type is begin -- Argument_String_To_List does not remove single quotes around an -- argument: do this now. if A (A'First) = '"' and then A (A'Last) = '"' then return (One_Arg, To_Unbounded_String (A (A'First + 1 .. A'Last - 1))); end if; return (Expandable, To_Unbounded_String (A)); end Process; Local_Args : Argument_List_Access; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Argument_List, Argument_List_Access); begin -- Get rid of the leading spaces, as this would result in multiple -- arguments in the call to Argument_String_To_List_With_Triple_Quotes -- Also remove trailing spaces, since otherwise the last argument on the -- command line, when surrounded with quotes, will be seen by Process as -- ending with ASCII.LF, and therefore the quotes will not be removed. Local_Args := Argument_String_To_List_With_Triple_Quotes (Trim (Text, Left => To_Set (' ' & ASCII.LF & ASCII.HT), Right => To_Set (' ' & ASCII.LF & ASCII.HT))); for J in Local_Args'Range loop CL.V.Append (Process (Local_Args (J).all)); Free (Local_Args (J)); end loop; Unchecked_Free (Local_Args); end Parse_Command_Line_String; ------------------ -- Parse_String -- ------------------ function Parse_String (Text : String; Mode : Command_Line_Mode) return Arg_List is CL : Arg_List; begin CL.Mode := Mode; if Mode = Separate_Args then Parse_Command_Line_String (CL, Text); else CL.V.Append ((One_Arg, To_Unbounded_String (Text))); end if; return CL; end Parse_String; ------------------ -- Parse_String -- ------------------ function Parse_String (Command : String; Text : String) return Arg_List is CL : Arg_List := Create (Command); begin Parse_Command_Line_String (CL, Text); return CL; end Parse_String; ----------------- -- Get_Command -- ----------------- function Get_Command (C : Arg_List) return String is begin if C.V.Is_Empty then return ""; else return To_String (C.V.Element (0).Text); end if; end Get_Command; ------------ -- Create -- ------------ function Create (Command : String) return Arg_List is C : Arg_List; begin C.V.Append ((One_Arg, To_Unbounded_String (Command))); return C; end Create; --------------------- -- Append_Argument -- --------------------- procedure Append_Argument (C : in out Arg_List; Argument : String; Mode : Argument_Mode) is begin C.V.Append ((Mode, To_Unbounded_String (Argument))); end Append_Argument; ------------- -- To_List -- ------------- function To_List (C : Arg_List; Include_Command : Boolean) return GNAT.OS_Lib.Argument_List is First : Natural; begin if Include_Command then First := 0; else First := 1; end if; declare L : GNAT.OS_Lib.Argument_List (1 .. Natural (C.V.Length) - First); begin for J in First .. Natural (C.V.Length) - 1 loop L (J + 1 - First) := new String' (To_String (C.V.Element (J).Text)); end loop; return L; end; end To_List; ----------------------- -- To_Display_String -- ----------------------- function To_Display_String (C : Arg_List; Include_Command : Boolean := True; Max_Arg_Length : Positive := Positive'Last) return String is Result : Unbounded_String := To_Unbounded_String (""); Start : Natural := 1; begin if not Include_Command then Start := 2; end if; for Index in Start .. Natural (C.V.Length) loop declare Arg_Len : constant Natural := Length (C.V.Element (Index - 1).Text); begin if Arg_Len > Max_Arg_Length then Append (Result, Unbounded_Slice (C.V.Element (Index - 1).Text, 1, Max_Arg_Length - 1)); Append (Result, "..."); else Append (Result, C.V.Element (Index - 1).Text); end if; end; if Index < Natural (C.V.Length) then Append (Result, " "); end if; end loop; return To_String (Result); end To_Display_String; --------------------- -- To_Debug_String -- --------------------- function To_Debug_String (C : Arg_List) return String is Result : Unbounded_String := To_Unbounded_String ("Command: "); begin Append (Result, C.V.Element (0).Text); for J in 1 .. Natural (C.V.Length) - 1 loop Append (Result, ASCII.LF & "Arg: " & C.V.Element (J).Text); end loop; return To_String (Result); end To_Debug_String; ---------------------- -- To_Script_String -- ---------------------- function To_Script_String (C : Arg_List) return String is function Arg (A : Unbounded_String) return Unbounded_String; -- Auxiliary function to process one arg --------- -- Arg -- --------- function Arg (A : Unbounded_String) return Unbounded_String is S : constant String := To_String (A); R : Unbounded_String; begin for J in S'Range loop case S (J) is when '\' => Append (R, "\\"); when ' ' => Append (R, "\ "); when '"' => Append (R, "\"""); when others => Append (R, S (J)); end case; end loop; return R; end Arg; Result : Unbounded_String; begin if C = Empty_Command_Line then return ""; end if; if C.Mode = Raw_String then return To_String (C.V.Element (0).Text); end if; -- Convert all arguments for J in 1 .. Natural (C.V.Length) loop Append (Result, Arg (C.V.Element (J - 1).Text)); Append (Result, ' '); end loop; -- Return result without the trailing space declare R : constant String := To_String (Result); begin return R (R'First .. R'Last - 1); end; end To_Script_String; ---------------- -- Substitute -- ---------------- procedure Substitute (CL : in out Arg_List; Char : Character; Callback : access function (Param : String; Mode : Command_Line_Mode) return Arg_List) is New_CL : Arg_List; function Expand_In_String (A : Unbounded_String) return Unbounded_String; -- Expand the argument in place in S and return the result ---------------------- -- Expand_In_String -- ---------------------- function Expand_In_String (A : Unbounded_String) return Unbounded_String is S : constant String := To_String (A); U : Unbounded_String; J : Natural; Beg : Natural; New_CL : Arg_List; Skip_Ending_Bracket : Boolean := False; begin if S = "" then return Null_Unbounded_String; end if; J := S'First; while J <= S'Last loop if S (J) = Char and then J < S'Last then -- Skip to the next separator J := J + 1; Beg := J; if S (J) = '{' then -- An '{' immediately follows the special character: -- the parameter should be the whole string contained -- between this and the ending '}'. Skip_Ending_Bracket := True; J := J + 1; Beg := J; while J <= S'Last and then S (J) /= '}' loop J := J + 1; end loop; else Skip_Ending_Bracket := False; while J <= S'Last and then (Is_Alphanumeric (S (J)) or else S (J) = '*' or else S (J) = '-' or else S (J) = '@') loop J := J + 1; end loop; end if; -- A doubling of the special character indicates that we -- are inserting it. if S (J - 1) = Char then Append (U, Char); J := J + 1; else New_CL := Callback (S (Beg .. J - 1), Raw_String); for K in 0 .. Natural (New_CL.V.Length) - 1 loop if CL.Mode = Raw_String then Append (U, Escape_Backslashes (New_CL.V.Element (K).Text)); else Append (U, New_CL.V.Element (K).Text); end if; if K < Natural (New_CL.V.Length) - 1 then Append (U, ' '); end if; end loop; end if; if Skip_Ending_Bracket then J := J + 1; end if; else Append (U, S (J)); J := J + 1; end if; end loop; return U; end Expand_In_String; begin New_CL.Mode := CL.Mode; if CL = Empty_Command_Line then return; end if; if Callback = null then return; end if; case CL.Mode is when Raw_String => declare U : constant Unbounded_String := Expand_In_String (CL.V.Element (0).Text); begin CL.V.Replace_Element (0, (One_Arg, U)); end; when Separate_Args => for J in 0 .. Natural (CL.V.Length) - 1 loop case CL.V.Element (J).Mode is when One_Arg => declare U : constant Unbounded_String := Expand_In_String (CL.V.Element (J).Text); begin New_CL.V.Append ((One_Arg, U)); end; when Expandable => declare P : constant Unbounded_String := CL.V.Element (J).Text; begin if Element (P, 1) = Char then New_CL.V.Append (Callback (Slice (P, 2, Length (P)), Separate_Args).V); else New_CL.V.Append ((Expandable, Expand_In_String (P))); end if; end; end case; end loop; CL := New_CL; end case; end Substitute; ----------------- -- Args_Length -- ----------------- function Args_Length (C : Arg_List) return Integer is begin return Natural (C.V.Length) - 1; end Args_Length; ------------- -- Nth_Arg -- ------------- function Nth_Arg (C : Arg_List; N : Natural) return String is begin return To_String (C.V.Element (N).Text); end Nth_Arg; function Nth_Arg (C : Arg_List; N : Natural) return Unbounded_String is begin return C.V.Element (N).Text; end Nth_Arg; ----------------- -- Set_Nth_Arg -- ----------------- procedure Set_Nth_Arg (C : in out Arg_List; N : Natural; Arg : String) is begin -- If there are not enough arguments, create them while N > Args_Length (C) loop C.V.Append ((One_Arg, Null_Unbounded_String)); end loop; C.V.Replace_Element (N, (C.V.Element (N).Mode, To_Unbounded_String (Arg))); end Set_Nth_Arg; ----------------------------- -- Argument_List_To_String -- ----------------------------- function Argument_List_To_String (List : GNAT.Strings.String_List; Protect_Quotes : Boolean := True) return String is Length : Natural := 0; begin for L in List'Range loop Length := Length + List (L)'Length + 1; if Protect_Quotes then for S in List (L)'Range loop if List (L)(S) = '"' or else List (L)(S) = ' ' or else List (L) (S) = '\' or else List (L) (S) = ''' then Length := Length + 1; end if; end loop; end if; end loop; declare S : String (1 .. Length); Index : Positive := S'First; begin for L in List'Range loop for J in List (L)'Range loop if Protect_Quotes then if List (L) (J) = '"' or else List (L) (J) = ' ' or else List (L) (J) = '\' or else List (L) (J) = ''' then S (Index) := '\'; Index := Index + 1; end if; end if; S (Index) := List (L)(J); Index := Index + 1; end loop; S (Index) := ' '; Index := Index + 1; end loop; return S (1 .. S'Last - 1); -- Ignore last space end; end Argument_List_To_String; end GNATCOLL.Arg_Lists; gnatcoll-core-21.0.0/src/gnatcoll-projects-normalize.adb0000644000175000017500000040503113661715457023076 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2002-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Exceptions; use Ada.Exceptions; with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Strings.Maps; use Ada.Strings.Maps; with Ada.Unchecked_Deallocation; with GNAT.Case_Util; use GNAT.Case_Util; with GNATCOLL.Traces; use GNATCOLL.Traces; with GNATCOLL.Utils; use GNATCOLL.Utils; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNATCOLL.VFS_Utils; use GNATCOLL.VFS_Utils; with GPR; use GPR; with GPR.Output; use GPR.Output; with GPR.Com; use GPR.Com; with GPR.Tree; use GPR.Tree; with GPR.Snames; with GPR.Names; use GPR.Names; with GNAT.Strings; use GNAT.Strings; package body GNATCOLL.Projects.Normalize is Me : constant Trace_Handle := Create ("Prj_Normalize"); Exception_Handle : constant Trace_Handle := Create ("UNEXPECTED_EXCEPTION", Default => On); type External_Variable_Value is record Variable_Type : GPR.Project_Node_Id; Variable_Name : GPR.Name_Id; Variable_Value : GPR.Name_Id; Negated : Boolean := False; end record; -- Description for one possible value of an external variable. Through an -- array of such values, it is possible to reference multiple case items in -- a case construction of a normalized project. -- If Negated is True, then Variable_Name must not be Variable_Value for -- the case item to match. -- See the example in the description of External_Variable_Value_Array. type External_Variable_Value_Array is array (Natural range <>) of External_Variable_Value; type External_Variable_Value_Array_Access is access External_Variable_Value_Array; -- Description for a case item (or a set of them). -- The same variable name can appear multiple times in the array. In that -- case, the value of the variable must match any of the choises for the -- case item to match. -- If a variable name exists in the case construction but not in the array, -- then the variable can have any value. -- -- As an example, given the following case construction: -- case V1 is -- when Val1 | Val2 => -- case V2 is -- when Val2_1 => stmt1; -- when others => stmt2; -- end case; -- when others => -- case V3 is -- when V3_1 => stmt3; -- when V3_2 => stmt4; -- end V3; -- end case; -- -- Then stmt1 can be reach with an External_Variable_Value_Array equal to: -- ((V1, Val1), (V1, Val2), (V2, Val2_1)) -- stmt2 can be reached with -- ((V1, Val1), (V1, Val2), (V2, Val2_1, True)) -- stmt3 can be reached with -- ((V1, Val1, True), (V1, Val2, True), (V3, V3_1)) -- Both stmt3 and stmt4 can be reached at the same time with -- ((V1, Val1, True), (V1, Val2, True)) -- -- If there was at least one non-negated element in the array, then at -- least one of the non-negated elements must be matched procedure Free is new Ada.Unchecked_Deallocation (External_Variable_Value_Array, External_Variable_Value_Array_Access); type Project_Node_Array is array (Positive range <>) of Project_Node_Id; type Project_Node_Array_Access is access Project_Node_Array; procedure Free is new Ada.Unchecked_Deallocation (Project_Node_Array, Project_Node_Array_Access); No_Value : constant External_Variable_Value := (Variable_Type => GPR.Empty_Project_Node, Variable_Name => GPR.No_Name, Variable_Value => GPR.No_Name, Negated => False); All_Case_Items : constant External_Variable_Value_Array (1 .. 0) := (others => No_Value); function Clone_Project (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id) return Project_Node_Id; -- Return a duplicate of Project and its declarations. We do not duplicate -- declarative items. -- The new project is not independent of the old one, since most of the -- nodes are shared between the two for efficiency reasons. procedure Add_Value (To : in out External_Variable_Value_Array_Access; Last : in out Natural; V : External_Variable_Value); -- Add V to the array To. To is reallocated as necessary. -- Last is the index of the last item that was set in To. function External_Variable_Name (Tree : GPR.Project_Node_Tree_Ref; Current_Project : Project_Node_Id; Ref : Project_Node_Id) return Name_Id; -- Return the name of the external variable referenced by Ref. -- The declaration of the variable is looked in Current_Project, unless -- another project is specified in the variable reference -- -- Ref should be a N_Variable_Reference. function Values_Matches (Tree : GPR.Project_Node_Tree_Ref; Var_Name : Name_Id; Case_Item : Project_Node_Id; Values : External_Variable_Value_Array) return Boolean; -- Return True if (Var_Name, Var_Value) is valid with regards to Values procedure Set_Uniq_Type_Name (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Var_Type : Project_Node_Id); -- Set the name for the N_String_Type_Declaration Var_Type, so that it is -- uniq in the project. -- Var_Type shouldn't have been added to the project yet. function Find_Node_By_Name (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Kind : Project_Node_Kind; Name : Name_Id) return Project_Node_Id; -- Find a node given its name type Matching_Item_Callback is access procedure (Item : GPR.Project_Node_Id); -- A callback function called for each case item that matches a specific -- set of values procedure For_Each_Matching_Case_Item (Tree : GPR.Project_Node_Tree_Ref; Project : GPR.Project_Node_Id; Pkg : GPR.Project_Node_Id := GPR.Empty_Project_Node; Case_Construct : in out GPR.Project_Node_Id; Values : External_Variable_Value_Array; Action : Matching_Item_Callback); -- Execute Action for all the case items in Project or Pkg that match -- Values. -- If no case item exists, Action is still called once on Project or Pkg -- itself. -- If Pkg is not the Empty_Node, then this subprogram only works on that -- package. However, the project must still be specified so that the -- declaration of the variables can be found. -- If a variable is referenced in Values, but doesn't have an associated -- case construction, a new case construction is added at the lowest level. -- -- Case_Construct is a pointer to the case statement inside Pkg. It should -- be the result of Find_Or_Create_Case_Statement. -- -- Important: Project must have been normalized first, and it is -- recommended to call Check_Case_Construction before -- -- Action can be null, in which case a side effect of this subprogram is to -- create the case constructs for the variables referenced in Values that -- do not already have a case construct. function Create_Case_Construction (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; External_Name : Name_Id; Var_Type : Project_Node_Id) return Project_Node_Id; -- Return a N_Case_Construction for the external variable Name. -- The declaration for the variable itself is added at the beginning of the -- project if no variable was found that already referenced Name. procedure Add_Case_Item (Tree : GPR.Project_Node_Tree_Ref; Case_Node : GPR.Project_Node_Id; Choice : GPR.Name_Id; Decl : GPR.Project_Node_Id := Empty_Project_Node); -- Create a new case item in case_node (which is associated with a -- "case var is" statement. If Decl is not empty, corresponding node will -- be cloned as this case alternative statements. procedure Add_To_Case_Items (Tree : GPR.Project_Node_Tree_Ref; Case_Construction : Project_Node_Id; Decl_List : Project_Node_Id); -- Copy all the declarative items from Decl_List into each of the case -- items of Case_Construction (at the beginning of each case item) procedure Set_Expression (Tree : GPR.Project_Node_Tree_Ref; Var_Or_Attribute : Project_Node_Id; Expr : Project_Node_Id); -- Set Var as the expression to use for the value of Var. This -- properly handles standard variables and variables defined through -- references to external environment variables. procedure Post_Process_After_Clone (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Pkg : Project_Node_Id := Empty_Project_Node); -- Post-process a project, and make sure that all the internal lists for -- variables, packages, types,... are properly chained up, and that all the -- variables reference a type declaration in Project (and not in some other -- project), ... -- On exit, Project is fully independent of whatever old project is was -- created from. function Find_Package_Declaration (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Name : GPR.Name_Id) return Project_Node_Id; -- Return the package whose name is Name, or Empty_Node if there is none function Find_Project_Of_Package (Data : Project_Tree_Data_Access; Project : Project_Type; Pkg_Name : String) return Project_Type; -- Return the id of the project that contains Pkg_Name. It will be -- different from Project if the package declaration is a renaming of -- another package. function Find_Case_Statement (Tree : GPR.Project_Node_Tree_Ref; Project : GPR.Project_Node_Id; Pkg : GPR.Project_Node_Id := GPR.Empty_Project_Node) return Project_Node_Id; -- Return the first case statement in Project/Pkg. -- In a normalized project, this returns the only case statement that -- exists in a package or project. procedure Move_From_Common_To_Case_Construct (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Pkg : Project_Node_Id; Case_Construct : in out Project_Node_Id; Scenario_Variables : Scenario_Variable_Array; Attribute_Name : GPR.Name_Id; Attribute_Index : GPR.Name_Id := No_Name); -- Move any declaration for the attribute from the common part of the -- project into each branch of the nested case construct. Nothing is done -- if there is no such declaration. procedure Add_Node_To_List (To : in out Project_Node_Array_Access; Last : in out Natural; Node : Project_Node_Id); -- Add a new node into the list of nodes To. -- To is resized as needed procedure For_Each_Scenario_Case_Item (Tree : GPR.Project_Node_Tree_Ref; Project : GPR.Project_Node_Id; Pkg : GPR.Project_Node_Id := GPR.Empty_Project_Node; Case_Construct : in out GPR.Project_Node_Id; Scenario_Variables : GNATCOLL.Projects.Scenario_Variable_Array; Action : Matching_Item_Callback); -- Same above, but it works directly for the current scenario (ie its gets -- the value of the variables directly from the environment). For -- efficiency, the list of scenario variables has to be provided as a -- parameter. -- Important: Project must have been normalized first, and it is -- recommended to call Check_Case_Construction before -- -- Case_Construct is a pointer to the case statement inside Pkg. It should -- be the result of Find_Or_Create_Case_Statement. -- -- Action can be null, in which case a side effect of this subprogram is to -- create the nested case for all the scenario variables. All case items -- are empty. type Node_Callback is access procedure (Node : Project_Node_Id); procedure For_Each_Directory_Node (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Type; Action : Node_Callback); -- For each node that deals with a procedure, calls Action function Attribute_Matches (Tree : GPR.Project_Node_Tree_Ref; Node : Project_Node_Id; Attribute_Name : Name_Id; Attribute_Index : Name_Id) return Boolean; -- Return True if Node is an attribute declaration matching Attribute_Name -- and Attribute_Index. -- If Attribute_Index is Any_Attribute, no matching is done on the index. function Find_Last_Declaration_Of (Tree : GPR.Project_Node_Tree_Ref; Parent : Project_Node_Id; Attr_Name : GPR.Name_Id; Attr_Index : GPR.Name_Id := No_Name) return Project_Node_Id; -- Find the last declaration for the attribute Attr_Name, in the -- declarative list contained in Parent. -- The returned value is the last such declaration, or Empty_Node if there -- was none. -- This returns the current item of the declarative item procedure Remove_Attribute_Declarations (Tree : GPR.Project_Node_Tree_Ref; Parent : Project_Node_Id; Attribute_Name : Name_Id; Attribute_Index : Name_Id); -- Remove all declarations for Attribute_Name in the declarative item list -- of Parent. -- If Attribute_Index is Any_Attribute, no matching is done on the index. type Set_Attribute_Callback is access procedure (Tree_Node : GPR.Project_Node_Tree_Ref; Project : Project_Type; Attribute_Name : Name_Id; Index_Id : Name_Id; Previous_Decl : Project_Node_Id; Case_Item : Project_Node_Id); procedure Internal_Set_Attribute (Tree : Project_Tree_Data_Access; Project : Project_Type; Attribute : String; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""; Callback : Set_Attribute_Callback); -- Internal version of Set_Attribute function String_As_Expression (Value : Name_Id; Tree : GPR.Project_Node_Tree_Ref) return Project_Node_Id; -- Return an N_Expression node that represents the static string Value. -- ??? Could be implemented in terms of Concatenate. function Get_All_Possible_Values (Tree : GPR.Project_Node_Tree_Ref; Variable : Project_Node_Id) return Name_Id_Array; -- Return the list of all possible values for Variable procedure Set_With_Clause_Path (Tree : GPR.Project_Node_Tree_Ref; With_Clause : Project_Node_Id; Imported_Project_Location : Virtual_File; Imported_Project : Project_Node_Id; Importing_Project : Project_Node_Id; Use_Relative_Path : Boolean; Use_Base_Name : Boolean; Limited_With : Boolean := False); -- Set the attributes of the with_clause (imported project node, imported -- project path,....) procedure Remove_Node (Tree : GPR.Project_Node_Tree_Ref; Parent : Project_Node_Id; Node : Project_Node_Id); -- Remove Node from the declaration list in Parent. -- This doesn't search recursively inside nested packages, case -- constructions, ... procedure Remove_Variable_Declaration (Tree : GPR.Project_Node_Tree_Ref; Project_Or_Package : Project_Node_Id; Declaration : Project_Node_Id); -- Remove the variable declaration from the list of variables in -- Project_Or_Package. ----------------------------------- -- For_Each_Environment_Variable -- ----------------------------------- procedure For_Each_Environment_Variable (Tree : GPR.Project_Node_Tree_Ref; Root_Project : Project_Type; Ext_Variable_Name : Name_Id; Specific_Choice : Name_Id; Action : Environment_Variable_Callback) is Variable_Nodes : Project_Node_Array_Access := new Project_Node_Array (1 .. 100); Variable_Nodes_Last : Natural := Variable_Nodes'First - 1; -- List of all the variables that reference Ext_Variable_Name -- in the current project. procedure Process_Expression (Project : Project_Node_Id; Expression : Project_Node_Id); -- Delete all references to the variable in Expr procedure Recurse_In_Project (Project : Project_Node_Id; Pkg_Or_Case_Item : Project_Node_Id); -- Delete the scenario variable in a specific part of Root_Project -- (either the project itself, if Pkg_Or_Case_Item is Empty_Node, -- or a package or a case item. function Is_Reference_To_Ext (Node : Project_Node_Id) return Boolean; -- Return True if Node is a reference (N_External_Value or -- N_Variable_Reference) to the external variable Ext_Variable_Name. -- Var_Declarations should contain the list of -- N_Typed_Variable_Declaration nodes that refer to Ext_Variable_Name. ------------------------- -- Is_Reference_To_Ext -- ------------------------- function Is_Reference_To_Ext (Node : Project_Node_Id) return Boolean is begin case Kind_Of (Node, Tree) is when N_External_Value => return String_Value_Of (GPR.Tree.External_Reference_Of (Node, Tree), Tree) = Ext_Variable_Name; when N_Variable_Reference => for J in Variable_Nodes'First .. Variable_Nodes_Last loop if GPR.Tree.Name_Of (Node, Tree) = GPR.Tree.Name_Of (Variable_Nodes (J), Tree) then return True; end if; end loop; return False; when others => return False; end case; end Is_Reference_To_Ext; ------------------------ -- Process_Expression -- ------------------------ procedure Process_Expression (Project : Project_Node_Id; Expression : Project_Node_Id) is Expr : Project_Node_Id := Expression; Term : Project_Node_Id; begin while Expr /= Empty_Project_Node loop Term := First_Term (Expr, Tree); while Term /= Empty_Project_Node loop case Kind_Of (Current_Term (Term, Tree), Tree) is -- Handles ("-g" & A, "-O2" & external ("A")) when N_Literal_String_List => Process_Expression (Project, First_Expression_In_List (Current_Term (Term, Tree), Tree)); -- Handles "-g" & external ("A") -- Replace A by the constant string representing its value when N_External_Value => if Is_Reference_To_Ext (Current_Term (Term, Tree)) then Action (Project, Term, Current_Term (Term, Tree), Empty_Project_Node); end if; -- Handles "-g" & Var -- Where Var is a reference to the external variable when N_Variable_Reference => if Is_Reference_To_Ext (Current_Term (Term, Tree)) then Action (Project, Term, Current_Term (Term, Tree), Empty_Project_Node); end if; when others => null; end case; Term := Next_Term (Term, Tree); end loop; Expr := Next_Expression_In_List (Expr, Tree); end loop; end Process_Expression; ------------------------ -- Recurse_In_Project -- ------------------------ procedure Recurse_In_Project (Project : Project_Node_Id; Pkg_Or_Case_Item : Project_Node_Id) is Decl, Current, Case_Item, Choice : Project_Node_Id; Match : Boolean; begin if Pkg_Or_Case_Item /= Empty_Project_Node then Decl := First_Declarative_Item_Of (Pkg_Or_Case_Item, Tree); else Decl := First_Declarative_Item_Of (Project_Declaration_Of (Project, Tree), Tree); end if; while Decl /= Empty_Project_Node loop Current := Current_Item_Node (Decl, Tree); case Kind_Of (Current, Tree) is when N_Typed_Variable_Declaration => if Is_External_Variable (Current, Tree) and then External_Reference_Of (Current, Tree) = Ext_Variable_Name then Add_Node_To_List (Variable_Nodes, Variable_Nodes_Last, Current); Action (Project, Empty_Project_Node, String_Type_Of (Current, Tree), Empty_Project_Node); Action (Project, Decl, Current, Empty_Project_Node); end if; Process_Expression (Project, Expression_Of (Current, Tree)); when N_Case_Construction => if Is_Reference_To_Ext (Case_Variable_Reference_Of (Current, Tree)) then Case_Item := First_Case_Item_Of (Current, Tree); while Case_Item /= Empty_Project_Node loop Choice := First_Choice_Of (Case_Item, Tree); -- If we have reached Empty_Node and nothing matched -- before, then that is the case item we want to keep. -- This corresponds to "when others" Match := Choice = Empty_Project_Node or else Specific_Choice = No_Name; if not Match then while Choice /= Empty_Project_Node loop if String_Value_Of (Choice, Tree) = Specific_Choice then Match := True; exit; end if; Choice := Next_Literal_String (Choice, Tree); end loop; end if; if Match then Action (Project, Decl, Case_Item, Choice); end if; Recurse_In_Project (Project, Case_Item); Case_Item := Next_Case_Item (Case_Item, Tree); end loop; else Case_Item := First_Case_Item_Of (Current, Tree); while Case_Item /= Empty_Project_Node loop Recurse_In_Project (Project, Case_Item); Case_Item := Next_Case_Item (Case_Item, Tree); end loop; end if; when N_Package_Declaration => Recurse_In_Project (Project, Current); when N_Variable_Declaration | N_Attribute_Declaration => Process_Expression (Project, Expression_Of (Current, Tree)); when others => null; end case; Decl := Next_Declarative_Item (Decl, Tree); end loop; end Recurse_In_Project; Iter : Inner_Project_Iterator := Start (Root_Project); begin while Current (Iter) /= No_Project loop Recurse_In_Project (Current (Iter).Node, Empty_Project_Node); Next (Iter); end loop; Free (Variable_Nodes); end For_Each_Environment_Variable; --------------------------------- -- Remove_Variable_Declaration -- --------------------------------- procedure Remove_Variable_Declaration (Tree : GPR.Project_Node_Tree_Ref; Project_Or_Package : Project_Node_Id; Declaration : Project_Node_Id) is Tmp, Next : Project_Node_Id; Pkg : Project_Node_Id := Project_Or_Package; begin while Pkg /= Empty_Project_Node loop Tmp := First_Variable_Of (Pkg, Tree); if Tmp = Declaration then Set_First_Variable_Of (Pkg, Tree, Next_Variable (Tmp, Tree)); return; else loop Next := Next_Variable (Tmp, Tree); exit when Next = Empty_Project_Node; if Next = Declaration then Set_Next_Variable (Tmp, Tree, Next_Variable (Next, Tree)); return; end if; end loop; end if; if Kind_Of (Pkg, Tree) = N_Project then Pkg := First_Package_Of (Pkg, Tree); else Pkg := Next_Package_In_Project (Pkg, Tree); end if; end loop; Trace (Me, "Remove_Variable_Declaration: did not find the declaration" & " for the variable"); end Remove_Variable_Declaration; ----------------- -- Remove_Node -- ----------------- procedure Remove_Node (Tree : GPR.Project_Node_Tree_Ref; Parent : Project_Node_Id; Node : Project_Node_Id) is P : Project_Node_Id := Parent; Decl, Next : Project_Node_Id; begin -- ??? Should reset the list of Variables and Types if the node matches if Kind_Of (Parent, Tree) = N_Project then P := Project_Declaration_Of (Parent, Tree); end if; Decl := First_Declarative_Item_Of (P, Tree); if Current_Item_Node (Decl, Tree) = Node then Set_First_Declarative_Item_Of (P, Tree, Next_Declarative_Item (Decl, Tree)); end if; while Decl /= Empty_Project_Node loop Next := Next_Declarative_Item (Decl, Tree); if Next /= Empty_Project_Node and then Current_Item_Node (Next, Tree) = Node then Set_Next_Declarative_Item (Decl, Tree, Next_Declarative_Item (Next, Tree)); exit; end if; Decl := Next; end loop; end Remove_Node; ----------------------------- -- Get_All_Possible_Values -- ----------------------------- function Get_All_Possible_Values (Tree : GPR.Project_Node_Tree_Ref; Variable : Project_Node_Id) return Name_Id_Array is Choice : Project_Node_Id := First_Literal_String (String_Type_Of (Variable, Tree), Tree); Choices_Count : Natural := 0; begin while Choice /= Empty_Project_Node loop Choices_Count := Choices_Count + 1; Choice := Next_Literal_String (Choice, Tree); end loop; declare Choices : Name_Id_Array (1 .. Choices_Count); Index : Natural := Choices'First; begin Choice := First_Literal_String (String_Type_Of (Variable, Tree), Tree); while Choice /= Empty_Project_Node loop Choices (Index) := String_Value_Of (Choice, Tree); Index := Index + 1; Choice := Next_Literal_String (Choice, Tree); end loop; return Choices; end; end Get_All_Possible_Values; -------------------------- -- String_As_Expression -- -------------------------- function String_As_Expression (Value : Name_Id; Tree : GPR.Project_Node_Tree_Ref) return Project_Node_Id is begin return Enclose_In_Expression (Create_Literal_String (Value, Tree), Tree); end String_As_Expression; ----------------------------------- -- Remove_Attribute_Declarations -- ----------------------------------- procedure Remove_Attribute_Declarations (Tree : GPR.Project_Node_Tree_Ref; Parent : Project_Node_Id; Attribute_Name : Name_Id; Attribute_Index : Name_Id) is Decl : Project_Node_Id := First_Declarative_Item_Of (Parent, Tree); Previous : Project_Node_Id := Empty_Project_Node; begin while Decl /= Empty_Project_Node loop if Attribute_Matches (Tree, Current_Item_Node (Decl, Tree), Attribute_Name, Attribute_Index) then if Previous = Empty_Project_Node then Set_First_Declarative_Item_Of (Parent, Tree, Next_Declarative_Item (Decl, Tree)); else Set_Next_Declarative_Item (Previous, Tree, Next_Declarative_Item (Decl, Tree)); end if; else Previous := Decl; end if; Decl := Next_Declarative_Item (Decl, Tree); end loop; end Remove_Attribute_Declarations; ------------------------------ -- Find_Last_Declaration_Of -- ------------------------------ function Find_Last_Declaration_Of (Tree : GPR.Project_Node_Tree_Ref; Parent : Project_Node_Id; Attr_Name : Name_Id; Attr_Index : Name_Id := No_Name) return Project_Node_Id is Decl, Expr : Project_Node_Id; Result : Project_Node_Id := Empty_Project_Node; begin Decl := First_Declarative_Item_Of (Parent, Tree); while Decl /= Empty_Project_Node loop Expr := Current_Item_Node (Decl, Tree); if Attribute_Matches (Tree, Expr, Attr_Name, Attr_Index) then Result := Expr; end if; Decl := Next_Declarative_Item (Decl, Tree); end loop; return Result; end Find_Last_Declaration_Of; ----------------------- -- Attribute_Matches -- ----------------------- function Attribute_Matches (Tree : GPR.Project_Node_Tree_Ref; Node : Project_Node_Id; Attribute_Name : Name_Id; Attribute_Index : Name_Id) return Boolean is begin return Kind_Of (Node, Tree) = N_Attribute_Declaration and then GPR.Tree.Name_Of (Node, Tree) = Attribute_Name and then (Attribute_Index = Get_String (Any_Attribute) or else (Attribute_Index = No_Name and then Associative_Array_Index_Of (Node, Tree) = No_Name) or else (Attribute_Index /= No_Name and then Associative_Array_Index_Of (Node, Tree) /= No_Name and then Associative_Array_Index_Of (Node, Tree) = Attribute_Index)); end Attribute_Matches; --------------------------------- -- For_Each_Scenario_Case_Item -- --------------------------------- procedure For_Each_Scenario_Case_Item (Tree : GPR.Project_Node_Tree_Ref; Project : GPR.Project_Node_Id; Pkg : GPR.Project_Node_Id := GPR.Empty_Project_Node; Case_Construct : in out GPR.Project_Node_Id; Scenario_Variables : Scenario_Variable_Array; Action : Matching_Item_Callback) is Values : External_Variable_Value_Array (1 .. Scenario_Variables'Length); Last_Values : Natural := Values'First - 1; begin for J in Scenario_Variables'Range loop Last_Values := Last_Values + 1; Values (Last_Values) := External_Variable_Value' (Variable_Type => Scenario_Variables (J).String_Type, Variable_Name => Scenario_Variables (J).Ext_Name, Variable_Value => Scenario_Variables (J).Value, Negated => False); end loop; For_Each_Matching_Case_Item (Tree, Project, Pkg, Case_Construct, Values, Action); end For_Each_Scenario_Case_Item; ---------------------- -- Add_Node_To_List -- ---------------------- procedure Add_Node_To_List (To : in out Project_Node_Array_Access; Last : in out Natural; Node : Project_Node_Id) is Old : Project_Node_Array_Access := To; begin if Last = To'Last then To := new Project_Node_Array (1 .. Old'Last * 2); To (1 .. Old'Length) := Old.all; Free (Old); end if; Last := Last + 1; To (Last) := Node; end Add_Node_To_List; ------------------------------ -- Find_Package_Declaration -- ------------------------------ function Find_Package_Declaration (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Name : GPR.Name_Id) return Project_Node_Id is begin return Find_Node_By_Name (Tree, Project, N_Package_Declaration, Name); end Find_Package_Declaration; -------------------- -- Set_Expression -- -------------------- procedure Set_Expression (Tree : GPR.Project_Node_Tree_Ref; Var_Or_Attribute : Project_Node_Id; Expr : Project_Node_Id) is E : Project_Node_Id; begin E := Expression_Of (Var_Or_Attribute, Tree); if E = Empty_Project_Node then Set_Expression_Of (Var_Or_Attribute, Tree, Expr); else case Kind_Of (E, Tree) is when N_Expression => Set_Expression_Of (Var_Or_Attribute, Tree, Expr); when N_External_Value => Set_External_Default_Of (E, Tree, Expr); when others => raise Program_Error; end case; end if; end Set_Expression; ------------------------------ -- Post_Process_After_Clone -- ------------------------------ procedure Post_Process_After_Clone (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Pkg : Project_Node_Id := Empty_Project_Node) is Last_Var : Project_Node_Id := Empty_Project_Node; Last_Type : Project_Node_Id := Empty_Project_Node; Last_Package : Project_Node_Id := Empty_Project_Node; Decl_Item : Project_Node_Id; Current_Node : Project_Node_Id; begin if Pkg = Empty_Project_Node then Decl_Item := First_Declarative_Item_Of (Project_Declaration_Of (Project, Tree), Tree); else pragma Assert (Kind_Of (Pkg, Tree) = N_Package_Declaration); Decl_Item := First_Declarative_Item_Of (Pkg, Tree); end if; while Decl_Item /= Empty_Project_Node loop Current_Node := Current_Item_Node (Decl_Item, Tree); case Kind_Of (Current_Node, Tree) is when N_Package_Declaration => if Last_Package /= Empty_Project_Node then Set_Next_Package_In_Project (Last_Package, Tree, Current_Node); Last_Package := Current_Node; else Last_Package := Current_Node; Tree.Project_Nodes.Table (Project).Packages := Last_Package; end if; Post_Process_After_Clone (Tree, Project, Last_Package); when N_Variable_Declaration | N_Typed_Variable_Declaration => if Last_Var /= Empty_Project_Node then Set_Next_Variable (Last_Var, Tree, Current_Node); Set_Next_Variable (Current_Node, Tree, Empty_Project_Node); Last_Var := Current_Node; else Last_Var := Current_Node; Set_Next_Variable (Last_Var, Tree, Empty_Project_Node); if Pkg /= Empty_Project_Node then Tree.Project_Nodes.Table (Pkg).Variables := Last_Var; else Tree.Project_Nodes.Table (Project).Variables := Last_Var; end if; end if; -- Make sure that we do reference the type defined in the new -- project, not in some older project if Kind_Of (Current_Node, Tree) = N_Typed_Variable_Declaration then Set_String_Type_Of (Current_Node, Tree, Find_Type_Declaration (Tree, Project, GPR.Tree.Name_Of (String_Type_Of (Current_Node, Tree), Tree))); end if; when N_Variable_Reference => if String_Type_Of (Current_Node, Tree) /= Empty_Project_Node then Set_String_Type_Of (Current_Node, Tree, Find_Type_Declaration (Tree, Project, GPR.Tree.Name_Of (String_Type_Of (Current_Node, Tree), Tree))); end if; if Package_Node_Of (Current_Node, Tree) /= Empty_Project_Node then Set_Package_Node_Of (Current_Node, Tree, Find_Package_Declaration (Tree, Project, GPR.Tree.Name_Of (Package_Node_Of (Current_Node, Tree), Tree))); end if; when N_Attribute_Reference => if Package_Node_Of (Current_Node, Tree) /= Empty_Project_Node then Set_Package_Node_Of (Current_Node, Tree, Find_Package_Declaration (Tree, Project, GPR.Tree.Name_Of (Package_Node_Of (Current_Node, Tree), Tree))); end if; when N_String_Type_Declaration => if Last_Type /= Empty_Project_Node then Set_Next_String_Type (Last_Type, Tree, Current_Node); Last_Type := Current_Node; else Last_Type := Current_Node; Set_First_String_Type_Of (Project, Tree, Last_Type); end if; when others => null; end case; Decl_Item := Next_Declarative_Item (Decl_Item, Tree); end loop; end Post_Process_After_Clone; ----------------------- -- Add_To_Case_Items -- ----------------------- procedure Add_To_Case_Items (Tree : GPR.Project_Node_Tree_Ref; Case_Construction : Project_Node_Id; Decl_List : Project_Node_Id) is Case_Item : Project_Node_Id; begin Case_Item := First_Case_Item_Of (Case_Construction, Tree); while Case_Item /= Empty_Project_Node loop Add_In_Front (Tree, Case_Item, Clone_Node (Tree, Decl_List, True)); Case_Item := Next_Case_Item (Case_Item, Tree); end loop; end Add_To_Case_Items; --------------------------- -- Create_Typed_Variable -- --------------------------- function Create_Typed_Variable (Tree : GPR.Project_Node_Tree_Ref; Prj_Or_Pkg : Project_Node_Id; Name : String; Typ : Project_Node_Id; Add_Before_First_Case_Or_Pkg : Boolean := False) return Project_Node_Id is Node : constant Project_Node_Id := Default_Project_Node (Tree, N_Typed_Variable_Declaration, GPR.Single); begin Set_Name_Of (Node, Tree, Get_String (Name)); Set_String_Type_Of (Node, Tree, Typ); Add_At_End (Tree, Prj_Or_Pkg, Node, Add_Before_First_Pkg => Add_Before_First_Case_Or_Pkg, Add_Before_First_Case => Add_Before_First_Case_Or_Pkg); Set_Next_Variable (Node, Tree, First_Variable_Of (Prj_Or_Pkg, Tree)); Set_First_Variable_Of (Prj_Or_Pkg, Tree, Node); return Node; end Create_Typed_Variable; ------------------- -- Add_Case_Item -- ------------------- procedure Add_Case_Item (Tree : GPR.Project_Node_Tree_Ref; Case_Node : Project_Node_Id; Choice : Name_Id; Decl : GPR.Project_Node_Id := Empty_Project_Node) is Item, S, In_List : Project_Node_Id; begin -- Add the new case item at the end of the list, so that the order of -- items is the same as in the type declaration (H222-027) Item := Default_Project_Node (Tree, N_Case_Item); S := Default_Project_Node (Tree, N_Literal_String); Set_String_Value_Of (S, Tree, Choice); Set_First_Choice_Of (Item, Tree, S); In_List := First_Case_Item_Of (Case_Node, Tree); if In_List = Empty_Project_Node then Set_First_Case_Item_Of (Case_Node, Tree, Item); else while Next_Case_Item (In_List, Tree) /= Empty_Project_Node loop if First_Choice_Of (Next_Case_Item (In_List, Tree), Tree) = Empty_Project_Node then -- Hitting the "when others" choice. It should be replaced with -- the first new case item added. exit; else In_List := Next_Case_Item (In_List, Tree); end if; end loop; Set_Next_Case_Item (In_List, Tree, Item); if Decl /= Empty_Project_Node then Add_In_Front (Tree, Item, Clone_Node (Tree, Decl, True)); end if; end if; end Add_Case_Item; ------------------ -- Add_In_Front -- ------------------ procedure Add_In_Front (Tree : GPR.Project_Node_Tree_Ref; Parent : Project_Node_Id; Node : Project_Node_Id) is Real_Parent : Project_Node_Id; New_Decl, Decl : Project_Node_Id; begin if Kind_Of (Node, Tree) /= N_Declarative_Item then New_Decl := Default_Project_Node (Tree, N_Declarative_Item); Set_Current_Item_Node (New_Decl, Tree, Node); else New_Decl := Node; end if; if Kind_Of (Parent, Tree) = N_Project then Real_Parent := Project_Declaration_Of (Parent, Tree); else Real_Parent := Parent; end if; Decl := New_Decl; while Next_Declarative_Item (Decl, Tree) /= Empty_Project_Node loop Decl := Next_Declarative_Item (Decl, Tree); end loop; Set_Next_Declarative_Item (Decl, Tree, First_Declarative_Item_Of (Real_Parent, Tree)); Set_First_Declarative_Item_Of (Real_Parent, Tree, New_Decl); end Add_In_Front; ------------------------------- -- Create_Variable_Reference -- ------------------------------- function Create_Variable_Reference (Tree : GPR.Project_Node_Tree_Ref; Var : Project_Node_Id) return Project_Node_Id is Ref : Project_Node_Id; begin Assert (Me, Kind_Of (Var, Tree) = N_Typed_Variable_Declaration or else Kind_Of (Var, Tree) = N_Variable_Declaration, "Create_Variable_Reference: unexpected node type " & Kind_Of (Var, Tree)'Img); Ref := Default_Project_Node (Tree, N_Variable_Reference); Set_Name_Of (Ref, Tree, GPR.Tree.Name_Of (Var, Tree)); Set_Expression_Kind_Of (Ref, Tree, Expression_Kind_Of (Var, Tree)); if Kind_Of (Var, Tree) = N_Typed_Variable_Declaration then Set_String_Type_Of (Ref, Tree, String_Type_Of (Var, Tree)); end if; return Ref; end Create_Variable_Reference; --------------------------- -- Set_Value_As_External -- --------------------------- procedure Set_Value_As_External (Tree : GPR.Project_Node_Tree_Ref; Var : Project_Node_Id; External_Name : String; Default : String := "") is Ext : Project_Node_Id; Str : Project_Node_Id; begin pragma Assert (Expression_Kind_Of (Var, Tree) = GPR.Single); -- Create the expression if required Ext := Default_Project_Node (Tree, N_External_Value, Single); Set_Expression (Tree, Var, Enclose_In_Expression (Ext, Tree)); Str := Create_Literal_String (Get_String (External_Name), Tree); Set_External_Reference_Of (Ext, Tree, Str); if Default /= "" then Str := Create_Literal_String (Get_String (Default), Tree); Set_External_Default_Of (Ext, Tree, Str); end if; end Set_Value_As_External; ------------------------------ -- Create_Case_Construction -- ------------------------------ function Create_Case_Construction (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; External_Name : Name_Id; Var_Type : Project_Node_Id) return Project_Node_Id is Construct, Str : Project_Node_Id; Item : Project_Node_Id := Empty_Project_Node; Ref : Name_Id; Decl : Project_Node_Id; New_Type : Project_Node_Id; begin -- Make sure there is a definition for this variable (and its type) at -- the top-level of the project (not in a package nor in another -- project). -- This is required so that normalized projects might be standalone. -- Check if there is already a definition for the variable, and if not -- add it. Note: we do this before testing for the type, since we are -- adding in front of the declarative items list. -- We cannot use Project.Variables since the lists are not created -- before the post-processing phase of the normalization. Decl := First_Declarative_Item_Of (Project_Declaration_Of (Project, Tree), Tree); while Decl /= Empty_Project_Node loop Item := Current_Item_Node (Decl, Tree); if Kind_Of (Item, Tree) = N_Typed_Variable_Declaration then Ref := External_Reference_Of (Item, Tree); exit when Ref /= No_Name and then Ref = External_Name; end if; Item := Empty_Project_Node; Decl := Next_Declarative_Item (Decl, Tree); end loop; -- If not, add the variable and its expression if Item = Empty_Project_Node then Get_Name_String (External_Name); Item := Create_Typed_Variable (Tree, Project, Name_Buffer (1 .. Name_Len), Var_Type, Add_Before_First_Case_Or_Pkg => True); Set_Value_As_External (Tree, Item, Name_Buffer (1 .. Name_Len)); -- Make sure the type is only used for that variable, so that is can -- be freely modified. If we already have a type by the same name, -- find a new name. New_Type := Clone_Node (Tree, Var_Type, True); Set_Uniq_Type_Name (Tree, Project, New_Type); Set_String_Type_Of (Item, Tree, New_Type); Add_In_Front (Tree, Project, New_Type); end if; Construct := Default_Project_Node (Tree, N_Case_Construction); Set_Case_Variable_Reference_Of (Construct, Tree, Create_Variable_Reference (Tree, Item)); Str := First_Literal_String (Var_Type, Tree); while Str /= Empty_Project_Node loop Add_Case_Item (Tree, Construct, String_Value_Of (Str, Tree)); Str := Next_Literal_String (Str, Tree); end loop; return Construct; end Create_Case_Construction; --------------------------------- -- For_Each_Matching_Case_Item -- --------------------------------- procedure For_Each_Matching_Case_Item (Tree : GPR.Project_Node_Tree_Ref; Project : GPR.Project_Node_Id; Pkg : GPR.Project_Node_Id := GPR.Empty_Project_Node; Case_Construct : in out GPR.Project_Node_Id; Values : External_Variable_Value_Array; Action : Matching_Item_Callback) is Var_Seen : External_Variable_Value_Array_Access := new External_Variable_Value_Array (Values'Range); Last_Var_Seen : Natural := Var_Seen'First - 1; -- Only the Variable_Name is relevant here, and this is used to detect -- the variables that didn't play any role in the current case construct procedure Process_Case_Recursive (Case_Stmt : Project_Node_Id); -- Act recursively on Case_Stmt and search within all its case items for -- the matching ones. function Create_Case_If_Necessary return Project_Node_Id; -- Create a new case construction if necessary (ie if some variable was -- referenced in Values, and there was no matching case construction so -- far). -- Empty_Node is returned if no case construction was created. ------------------------------ -- Create_Case_If_Necessary -- ------------------------------ function Create_Case_If_Necessary return Project_Node_Id is Match : Boolean; begin for J in Values'Range loop Match := False; for K in Var_Seen'First .. Last_Var_Seen loop if Values (J).Variable_Name = Var_Seen (K).Variable_Name then Match := True; exit; end if; end loop; if not Match then return Create_Case_Construction (Tree, Project, Values (J).Variable_Name, Values (J).Variable_Type); end if; end loop; return Empty_Project_Node; end Create_Case_If_Necessary; ---------------------------- -- Process_Case_Recursive -- ---------------------------- procedure Process_Case_Recursive (Case_Stmt : Project_Node_Id) is Name : constant Name_Id := External_Variable_Name (Tree, Project, Case_Variable_Reference_Of (Case_Stmt, Tree)); Current_Item, New_Case : Project_Node_Id; Handling_Done : Boolean; begin pragma Assert (Name /= No_Name); -- Memorise the name of the variable we are processing, so that we -- can create missing case constructions at the end Add_Value (Var_Seen, Last_Var_Seen, (Empty_Project_Node, Name, No_Name, False)); -- For all possible values of the variable Current_Item := First_Case_Item_Of (Case_Stmt, Tree); while Current_Item /= Empty_Project_Node loop if Values_Matches (Tree, Name, Current_Item, Values) then Handling_Done := False; New_Case := First_Declarative_Item_Of (Current_Item, Tree); -- Are there any nested case ? while New_Case /= Empty_Project_Node loop if Kind_Of (Current_Item_Node (New_Case, Tree), Tree) = N_Case_Construction then Process_Case_Recursive (Current_Item_Node (New_Case, Tree)); Handling_Done := True; exit; end if; New_Case := Next_Declarative_Item (New_Case, Tree); end loop; if not Handling_Done then New_Case := Create_Case_If_Necessary; Handling_Done := New_Case /= Empty_Project_Node; if Handling_Done then -- Move all the declarative items currently in the case -- item to the nested case construction, so that we only -- have declarative items in the most-nested case -- constructions. if First_Declarative_Item_Of (Current_Item, Tree) /= Empty_Project_Node then Add_To_Case_Items (Tree, New_Case, First_Declarative_Item_Of (Current_Item, Tree)); Set_First_Declarative_Item_Of (Current_Item, Tree, Empty_Project_Node); end if; Add_At_End (Tree, Current_Item, New_Case); Process_Case_Recursive (New_Case); end if; end if; -- We can now report the matching case item if not Handling_Done and then Action /= null then Action (Current_Item); end if; end if; Current_Item := Next_Case_Item (Current_Item, Tree); end loop; Last_Var_Seen := Last_Var_Seen - 1; end Process_Case_Recursive; Top : Project_Node_Id; begin if Pkg /= Empty_Project_Node then Top := Pkg; else Top := Project_Declaration_Of (Project, Tree); end if; if Case_Construct = Empty_Project_Node then Case_Construct := Create_Case_If_Necessary; if Case_Construct /= Empty_Project_Node then Add_At_End (Tree, Top, Case_Construct); end if; end if; if Case_Construct = Empty_Project_Node then if Action /= null then Action (Top); end if; else Process_Case_Recursive (Case_Construct); end if; Free (Var_Seen); end For_Each_Matching_Case_Item; ----------------------- -- Find_Node_By_Name -- ----------------------- function Find_Node_By_Name (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Kind : Project_Node_Kind; Name : Name_Id) return Project_Node_Id is Decl : Project_Node_Id := First_Declarative_Item_Of (Project_Declaration_Of (Project, Tree), Tree); Current : Project_Node_Id; begin while Decl /= Empty_Project_Node loop Current := Current_Item_Node (Decl, Tree); if Kind_Of (Current, Tree) = Kind and then GPR.Tree.Name_Of (Current, Tree) = Name then return Current; end if; Decl := Next_Declarative_Item (Decl, Tree); end loop; return Empty_Project_Node; end Find_Node_By_Name; --------------------------- -- Find_Type_Declaration -- --------------------------- function Find_Type_Declaration (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Name : GPR.Name_Id) return Project_Node_Id is begin return Find_Node_By_Name (Tree, Project, N_String_Type_Declaration, Name); end Find_Type_Declaration; ------------------------ -- Get_Uniq_Type_Name -- ------------------------ procedure Set_Uniq_Type_Name (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Var_Type : Project_Node_Id) is Candidate : Name_Id; Attempt : Natural := 1; begin -- Check the type itself Candidate := Name_Of (Var_Type, Tree); while Find_Type_Declaration (Tree, Project, Candidate) /= Empty_Project_Node loop Get_Name_String (Candidate); Get_Name_String (Name_Of (Var_Type, Tree)); Add_Str_To_Name_Buffer (Image (Attempt, Min_Width => 0)); Attempt := Attempt + 1; Candidate := Name_Find; end loop; Set_Name_Of (Var_Type, Tree, Candidate); end Set_Uniq_Type_Name; -------------------- -- Values_Matches -- -------------------- function Values_Matches (Tree : GPR.Project_Node_Tree_Ref; Var_Name : Name_Id; Case_Item : Project_Node_Id; Values : External_Variable_Value_Array) return Boolean is -- The rule is the following: if there is any non-negated item, -- then we must match at least one of them. If there are none, -- then the case item matches if non of the negated item matches Match : Boolean := True; Choice : Project_Node_Id := First_Choice_Of (Case_Item, Tree); begin Choice_Loop : while Choice /= Empty_Project_Node loop for J in Values'Range loop if Values (J).Variable_Name = Var_Name then -- Change the default value if needed Match := Values (J).Negated; if Values (J).Variable_Value = String_Value_Of (Choice, Tree) then Match := not Values (J).Negated; exit Choice_Loop; end if; end if; end loop; Choice := Next_Literal_String (Choice, Tree); end loop Choice_Loop; return Match; end Values_Matches; ---------------------------- -- External_Variable_Name -- ---------------------------- function External_Variable_Name (Tree : GPR.Project_Node_Tree_Ref; Current_Project : Project_Node_Id; Ref : Project_Node_Id) return Name_Id is N : constant Name_Id := GPR.Tree.Name_Of (Ref, Tree); Pkg : Project_Node_Id; Variable : Variable_Node_Id; Recurse_In_Pkg : Boolean := False; begin if Package_Node_Of (Ref, Tree) /= Empty_Project_Node then Pkg := Package_Node_Of (Ref, Tree); elsif Project_Node_Of (Ref, Tree) /= Empty_Project_Node then Pkg := Project_Node_Of (Ref, Tree); else Pkg := Current_Project; Recurse_In_Pkg := True; end if; -- Should the project parser set the package_of field when the variable -- is defined inside a package ? Currently, it only sets this field if -- it is specified in the file itself. while Pkg /= Empty_Project_Node loop Variable := First_Variable_Of (Pkg, Tree); while Variable /= Empty_Project_Node loop if (Kind_Of (Variable, Tree) = N_Variable_Declaration or else Kind_Of (Variable, Tree) = N_Typed_Variable_Declaration) and then GPR.Tree.Name_Of (Variable, Tree) = N then return External_Reference_Of (Variable, Tree); end if; Variable := Next_Variable (Variable, Tree); end loop; if Recurse_In_Pkg then if Pkg = Current_Project then Pkg := First_Package_Of (Pkg, Tree); else Pkg := Next_Package_In_Project (Pkg, Tree); end if; end if; end loop; return No_Name; end External_Variable_Name; --------------- -- Add_Value -- --------------- procedure Add_Value (To : in out External_Variable_Value_Array_Access; Last : in out Natural; V : External_Variable_Value) is Old : External_Variable_Value_Array_Access := To; New_Last : Natural; begin if To = null or else Last = To'Last then if To = null or else To'Last = 0 then New_Last := 20; else New_Last := Old'Last * 2; end if; To := new External_Variable_Value_Array (Old'First .. New_Last); To (Old'Range) := Old.all; Free (Old); end if; Last := Last + 1; To (Last) := V; end Add_Value; ------------------- -- Clone_Project -- ------------------- function Clone_Project (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id) return Project_Node_Id is Project2, Decl : Project_Node_Id; begin Project2 := Clone_Node (Tree, Project); Decl := Clone_Node (Tree, Project_Declaration_Of (Project, Tree)); Set_Project_Declaration_Of (Project2, Tree, Decl); Set_First_Declarative_Item_Of (Decl, Tree, Empty_Project_Node); return Project2; end Clone_Project; --------------- -- Normalize -- --------------- procedure Normalize (Tree : Project_Tree_Data_Access; Project : Project_Type) is Tree_Node : constant GPR.Project_Node_Tree_Ref := GNATCOLL.Projects.Tree (Tree); Values : External_Variable_Value_Array_Access := null; Last_Values : Natural; -- Representation of the state of the case construction that is being -- parsed. -- Each time a new case item is seen in the original project, an entry -- is added into this array for all the possible values of the variable. -- On exit of the case item, the items are negated, so that it is still -- possible to process "when others". -- On exit of the case construction, the entries for the variable are -- removed from this list. Project_Norm : Project_Node_Id; Current_Pkg : Project_Node_Id; Case_Construct : Project_Node_Id; Decl : Project_Node_Id; Project_Node : Project_Node_Id; Max_Scenario_Variables : Integer; procedure Process_Declarative_List (From, To : Project_Node_Id); -- Process a declarative list (a project, a package, a case item,...). -- From is the first N_Declarative_Item in the list. To is the node to -- which the normalized declarative items are added. -- Note: It skips subpackages, and doesn't process them recursively, -- you should call this procedure once per subpackage. -- -- The output is however added to the case construction Case_Stmt if it -- is not Empty_Node, or if the Values is not null (the first case -- represent the case where there was a case statement before in the -- project file, the second one represents the first case statement). procedure Check_Index_Sensitivity (Decl_Item : Project_Node_Id); -- If Decl_Item is the declaration of an attribute the index of which -- is case-insensitive, convert that index to lower-case so that GPS -- properly finds references to it later on. ----------------------------- -- Check_Index_Sensitivity -- ----------------------------- procedure Check_Index_Sensitivity (Decl_Item : Project_Node_Id) is Current : constant Project_Node_Id := Current_Item_Node (Decl_Item, Tree_Node); begin if Kind_Of (Current, Tree_Node) = N_Attribute_Declaration then -- It is possible that the attribute doesn't have an index in the -- case of -- for Switches use Imported.Compiler'Switches; -- whereas in general Switches excepts an index. if Case_Insensitive (Current, Tree_Node) and then Associative_Array_Index_Of (Current, Tree_Node) /= No_Name then Get_Name_String (Associative_Array_Index_Of (Current, Tree_Node)); To_Lower (Name_Buffer (1 .. Name_Len)); Set_Associative_Array_Index_Of (Current, Tree_Node, Name_Find); end if; end if; end Check_Index_Sensitivity; ------------------------------ -- Process_Declarative_List -- ------------------------------ procedure Process_Declarative_List (From, To : Project_Node_Id) is Decl_Item, Current : Project_Node_Id := From; Next_Item, Choice : Project_Node_Id; Name : Name_Id; Case_Item : Project_Node_Id; Index : Natural; Var_Type : Project_Node_Id; Already_Have_Var, Match : Boolean; Decl_Item2 : Project_Node_Id; procedure Add_Decl_Item (To_Case_Item : Project_Node_Id); -- Add Decl_Item to To_Case_Item ------------------- -- Add_Decl_Item -- ------------------- procedure Add_Decl_Item (To_Case_Item : Project_Node_Id) is begin Add_At_End (Tree_Node, To_Case_Item, Clone_Node (Tree_Node, Decl_Item, True)); end Add_Decl_Item; begin -- Nothing to do if there is no project if From = Empty_Project_Node then return; end if; pragma Assert (Kind_Of (Decl_Item, Tree_Node) = N_Declarative_Item); while Decl_Item /= Empty_Project_Node loop Current := Current_Item_Node (Decl_Item, Tree_Node); -- Save the next item, since the current item will be inserted in -- a different list, and thus its next field will be modified. Next_Item := Next_Declarative_Item (Decl_Item, Tree_Node); Set_Next_Declarative_Item (Decl_Item, Tree_Node, Empty_Project_Node); case Kind_Of (Current, Tree_Node) is when N_Package_Declaration => -- Skip subpackages, since these must appear after every -- other declarative item in the normalized project. null; when N_Case_Construction => Name := External_Variable_Name (Tree_Node, Project_Norm, Case_Variable_Reference_Of (Current, Tree_Node)); if Name = No_Name then Trace (Me, "Normalizing a project with a non-scenario " & "variable in case construction"); Raise_Exception (Normalize_Error'Identity, Project.Name & GPR.Project_File_Extension & ": " & "Case constructions referencing non-external" & " variables can not be modified"); end if; Var_Type := String_Type_Of (Case_Variable_Reference_Of (Current, Tree_Node), Tree_Node); -- Do we already have this variable in values -- If yes, this means we have something similar to: -- case A is -- when "1" => -- case A is -- when "1" => keep; -- when "2" => ignore; -- We should only keep the item in the nested case that -- matches the value of the outer item. Already_Have_Var := False; for J in Values'First .. Last_Values loop if Values (J).Variable_Name = Name then Already_Have_Var := True; exit; end if; end loop; Case_Item := First_Case_Item_Of (Current, Tree_Node); -- For all the case items in the current case construction while Case_Item /= Empty_Project_Node loop Index := Last_Values + 1; if Already_Have_Var then Match := Values_Matches (Tree_Node, Name, Case_Item, Values (Values'First .. Last_Values)); else Match := True; Choice := First_Choice_Of (Case_Item, Tree_Node); while Choice /= Empty_Project_Node loop Add_Value (Values, Last_Values, External_Variable_Value' (Var_Type, Name, String_Value_Of (Choice, Tree_Node), False)); Choice := Next_Literal_String (Choice, Tree_Node); end loop; end if; if Match then -- Process the declarative list of items Process_Declarative_List (First_Declarative_Item_Of (Case_Item, Tree_Node), To); -- Negate all the values for J in Index .. Last_Values loop Values (J).Negated := True; end loop; end if; Case_Item := Next_Case_Item (Case_Item, Tree_Node); end loop; -- Remove all the entries for the variable in the array -- Note that we do not need to use String_Equal, since we -- know exactly the Name_Id we started with. if not Already_Have_Var then while Last_Values >= Values'First and then Values (Last_Values).Variable_Name = Name loop Last_Values := Last_Values - 1; end loop; end if; when N_Typed_Variable_Declaration => declare Save_Type : constant Project_Node_Id := String_Type_Of (Current, Tree_Node); begin -- Make sure that the type declaration is unique for that -- typed variable, since if we decide to remove the -- variable we should remove the type as well. Var_Type := Clone_Node (Tree_Node, String_Type_Of (Current, Tree_Node), True); Set_Uniq_Type_Name (Tree_Node, Project_Norm, Var_Type); Set_String_Type_Of (Current, Tree_Node, Var_Type); Add_At_End (Tree_Node, Project_Norm, Var_Type, Add_Before_First_Pkg => True, Add_Before_First_Case => True); -- Scenario variables must be defined at the project -- level if Current_Pkg /= Empty_Project_Node and then Is_External_Variable (Current, Tree_Node) then Add_At_End (Tree_Node, Project_Norm, Clone_Node (Tree_Node, Decl_Item, True), Add_Before_First_Pkg => True, Add_Before_First_Case => True); else For_Each_Matching_Case_Item (Tree_Node, Project_Norm, Current_Pkg, Case_Construct, Values (Values'First .. Last_Values), Add_Decl_Item'Unrestricted_Access); end if; Set_String_Type_Of (Current, Tree_Node, Save_Type); end; when N_String_Type_Declaration => null; when others => -- Add as many items as possible at once. This speeds things -- up, since we do not have to traverse all the case items -- for all of them. Check_Index_Sensitivity (Decl_Item); Decl_Item2 := Decl_Item; while Next_Item /= Empty_Project_Node loop case Kind_Of (Current_Item_Node (Next_Item, Tree_Node), Tree_Node) is when N_Package_Declaration | N_Case_Construction | N_Typed_Variable_Declaration | N_String_Type_Declaration => exit; when others => Check_Index_Sensitivity (Next_Item); end case; Set_Next_Declarative_Item (Decl_Item2, Tree_Node, Next_Item); Decl_Item2 := Next_Item; Next_Item := Next_Declarative_Item (Next_Item, Tree_Node); end loop; Set_Next_Declarative_Item (Decl_Item2, Tree_Node, Empty_Project_Node); For_Each_Matching_Case_Item (Tree_Node, Project_Norm, Current_Pkg, Case_Construct, Values (Values'First .. Last_Values), Add_Decl_Item'Unrestricted_Access); end case; Decl_Item := Next_Item; end loop; end Process_Declarative_List; begin if not Project.Data.Normalized then Max_Scenario_Variables := Scenario_Variables (Tree)'Length; Project_Node := Project.Node; Trace (Me, "Normalize: imported=" & Project.Name); Values := new External_Variable_Value_Array (1 .. Max_Scenario_Variables); Last_Values := Values'First - 1; Project_Norm := Clone_Project (Tree_Node, Project_Node); Current_Pkg := Empty_Project_Node; Case_Construct := Empty_Project_Node; Project.Data.Normalized := True; -- The top-level part of the project Process_Declarative_List (From => First_Declarative_Item_Of (Project_Declaration_Of (Project_Node, Tree_Node), Tree_Node), To => Project_Declaration_Of (Project_Norm, Tree_Node)); if Last_Values /= Values'First - 1 then Free (Values); Raise_Exception (Normalize_Error'Identity, "Internal error while normalizing"); end if; -- All the subpackages. Note that the project parser has at some -- point reverted the order of the package nodes, so we need to -- take that into account to preserve the order of packages in the -- file. Current_Pkg := First_Package_Of (Project_Node, Tree_Node); while Current_Pkg /= Empty_Project_Node loop Decl := First_Declarative_Item_Of (Current_Pkg, Tree_Node); Set_First_Declarative_Item_Of (Current_Pkg, Tree_Node, Empty_Project_Node); Case_Construct := Empty_Project_Node; Process_Declarative_List (From => Decl, To => Current_Pkg); if Last_Values /= Values'First - 1 then Free (Values); Raise_Exception (Normalize_Error'Identity, "Internal error while normalizing"); end if; declare Next_Pkg : constant Project_Node_Id := Next_Package_In_Project (Current_Pkg, Tree_Node); begin Set_Next_Package_In_Project (Current_Pkg, Tree_Node, Empty_Project_Node); Add_At_End (Tree_Node, Project_Declaration_Of (Project_Norm, Tree_Node), Current_Pkg, Add_Before_First_Pkg => True, Add_Before_First_Case => False); Current_Pkg := Next_Pkg; end; end loop; Free (Values); Post_Process_After_Clone (Tree_Node, Project_Norm); -- Directly replace in the table, so that all references to this -- project are automatically updated. There is a small memory -- leak, but since most of the project tree is shared, it doesn't -- really matter in the life of the project editor Tree_Node.Project_Nodes.Table (Project_Node) := Tree_Node.Project_Nodes.Table (Project_Norm); Trace (Me, "Done normalizing " & Project.Name); end if; end Normalize; ---------------- -- Clone_Node -- ---------------- function Clone_Node (Tree : GPR.Project_Node_Tree_Ref; Node : Project_Node_Id; Deep_Clone : Boolean := False) return Project_Node_Id is New_Node : Project_Node_Id; begin if Node = Empty_Project_Node then return Empty_Project_Node; end if; Tree_Private_Part.Project_Node_Table.Increment_Last (Tree.Project_Nodes); New_Node := Tree_Private_Part.Project_Node_Table.Last (Tree.Project_Nodes); -- Simple copy of all the fields. There is no need to duplicate -- Name_Id at this point, since nobody will modify them later on -- anyway. So we save some memory and keep them as is. -- Only the node ids will need to be copied for deep copies. Tree.Project_Nodes.Table (New_Node) := Tree.Project_Nodes.Table (Node); if Deep_Clone then case Kind_Of (Node, Tree) is when N_Project => -- Packages, Variables, First_String_Type_Of must be outside of -- this subprogram. Set_First_With_Clause_Of (New_Node, Tree, Clone_Node (Tree, First_With_Clause_Of (Node, Tree), True)); Set_Project_Declaration_Of (New_Node, Tree, Clone_Node (Tree, Project_Declaration_Of (Node, Tree), True)); Set_First_String_Type_Of (New_Node, Tree, Empty_Project_Node); when N_With_Clause => Set_Next_With_Clause_Of (New_Node, Tree, Clone_Node (Tree, Next_With_Clause_Of (Node, Tree), True)); when N_Project_Declaration => Set_First_Declarative_Item_Of (New_Node, Tree, Clone_Node (Tree, First_Declarative_Item_Of (Node, Tree), True)); when N_Declarative_Item => Set_Current_Item_Node (New_Node, Tree, Clone_Node (Tree, Current_Item_Node (Node, Tree), True)); Set_Next_Declarative_Item (New_Node, Tree, Clone_Node (Tree, Next_Declarative_Item (Node, Tree), True)); when N_Package_Declaration => -- Next_Package_In_Project and Variables must be set outside of -- this subprogram -- Pkg_Id doesn't need to be cloned, as per 9509-010. Set_First_Declarative_Item_Of (New_Node, Tree, Clone_Node (Tree, First_Declarative_Item_Of (Node, Tree), True)); Set_Next_Package_In_Project (New_Node, Tree, Empty_Project_Node); when N_String_Type_Declaration => -- Next_String_Type must be set outside of this Set_First_Literal_String (New_Node, Tree, Clone_Node (Tree, First_Literal_String (Node, Tree), True)); Set_Next_String_Type (New_Node, Tree, Empty_Project_Node); when N_Literal_String => Set_Next_Literal_String (New_Node, Tree, Clone_Node (Tree, Next_Literal_String (Node, Tree), True)); when N_Attribute_Declaration => Set_Expression_Of (New_Node, Tree, Clone_Node (Tree, Expression_Of (Node, Tree), True)); when N_Typed_Variable_Declaration => -- Next_Variable must be set outside of this -- String_Type_Of is set to the same value as for Node, and -- this needs to be fixed in a post-processing phase. Set_Expression_Of (New_Node, Tree, Clone_Node (Tree, Expression_Of (Node, Tree), True)); Set_String_Type_Of (New_Node, Tree, String_Type_Of (Node, Tree)); Set_Next_Variable (New_Node, Tree, Empty_Project_Node); when N_Variable_Declaration => -- Next_Variable must be set outside of this Set_Expression_Of (New_Node, Tree, Clone_Node (Tree, Expression_Of (Node, Tree), True)); Set_Next_Variable (New_Node, Tree, Empty_Project_Node); when N_Expression => Set_First_Term (New_Node, Tree, Clone_Node (Tree, First_Term (Node, Tree), True)); Set_Next_Expression_In_List (New_Node, Tree, Clone_Node (Tree, Next_Expression_In_List (Node, Tree), True)); when N_Term => Set_Current_Term (New_Node, Tree, Clone_Node (Tree, Current_Term (Node, Tree), True)); Set_Next_Term (New_Node, Tree, Clone_Node (Tree, Next_Term (Node, Tree), True)); when N_Literal_String_List => Set_First_Expression_In_List (New_Node, Tree, Clone_Node (Tree, First_Expression_In_List (Node, Tree), True)); when N_Variable_Reference => -- String_Type_Of is set to the same value as for Node, and -- this needs to be fixed in a post-processing phase. -- Same for Package_Node_Of null; when N_External_Value => Set_External_Reference_Of (New_Node, Tree, Clone_Node (Tree, External_Reference_Of (Node, Tree), True)); Set_External_Default_Of (New_Node, Tree, Clone_Node (Tree, External_Default_Of (Node, Tree), True)); when N_Attribute_Reference => -- Package_Node_Of is set to the same value of for Node, and -- this needs to be fixed in a post-processing phase. null; when N_Case_Construction => Set_Case_Variable_Reference_Of (New_Node, Tree, Clone_Node (Tree, Case_Variable_Reference_Of (Node, Tree), True)); Set_First_Case_Item_Of (New_Node, Tree, Clone_Node (Tree, First_Case_Item_Of (Node, Tree), True)); when N_Case_Item => Set_First_Choice_Of (New_Node, Tree, Clone_Node (Tree, First_Choice_Of (Node, Tree), True)); Set_First_Declarative_Item_Of (New_Node, Tree, Clone_Node (Tree, First_Declarative_Item_Of (Node, Tree), True)); when N_Split => Set_String_Argument_Of (New_Node, Tree, Clone_Node (Tree, String_Argument_Of (Node, Tree), True)); Set_Separator_Of (New_Node, Tree, Clone_Node (Tree, Separator_Of (Node, Tree), True)); when N_Comment_Zones => null; when N_Comment => null; end case; end if; return New_Node; end Clone_Node; ---------------- -- Get_String -- ---------------- function Get_String (Str : String) return Name_Id is begin if Str = "" then return No_Name; else Name_Len := Str'Length; Name_Buffer (1 .. Name_Len) := Str; return Name_Find; end if; end Get_String; ---------------- -- Get_String -- ---------------- function Get_String (Id : GPR.Name_Id) return String is begin if Id = No_Name then return ""; end if; return Get_Name_String (Id); exception when E : others => Trace (Exception_Handle, E); return ""; end Get_String; -------------------------- -- Is_External_Variable -- -------------------------- function Is_External_Variable (Var : GPR.Project_Node_Id; Tree : GPR.Project_Node_Tree_Ref) return Boolean is begin return Kind_Of (Current_Term (First_Term (Expression_Of (Var, Tree), Tree), Tree), Tree) = N_External_Value; end Is_External_Variable; --------------------------- -- External_Reference_Of -- --------------------------- function External_Reference_Of (Var : GPR.Project_Node_Id; Tree : GPR.Project_Node_Tree_Ref) return GPR.Name_Id is Expr : Project_Node_Id := Expression_Of (Var, Tree); begin Expr := First_Term (Expr, Tree); Expr := Current_Term (Expr, Tree); if Kind_Of (Expr, Tree) = N_External_Value then Expr := External_Reference_Of (Expr, Tree); return String_Value_Of (Expr, Tree); else return No_Name; end if; end External_Reference_Of; ----------------------------- -- Find_Project_Of_Package -- ----------------------------- function Find_Project_Of_Package (Data : Project_Tree_Data_Access; Project : Project_Type; Pkg_Name : String) return Project_Type is Pkg : Project_Node_Id; begin if Pkg_Name /= "" then Pkg := Find_Package_Declaration (Tree (Data), Project.Node, Get_String (Pkg_Name)); if Pkg /= Empty_Project_Node then Pkg := Project_Of_Renamed_Package_Of (Pkg, Tree (Data)); if Pkg /= Empty_Project_Node then return Project_Type (Project_From_Name (Data, GPR.Tree.Name_Of (Pkg, Tree (Data)))); end if; end if; end if; return Project; end Find_Project_Of_Package; ------------------------- -- Find_Case_Statement -- ------------------------- function Find_Case_Statement (Tree : GPR.Project_Node_Tree_Ref; Project : GPR.Project_Node_Id; Pkg : GPR.Project_Node_Id := GPR.Empty_Project_Node) return Project_Node_Id is Top : Project_Node_Id; Decl_Item : Project_Node_Id; Case_Construct : Project_Node_Id := Empty_Project_Node; begin if Pkg /= Empty_Project_Node then Top := Pkg; else Top := Project_Declaration_Of (Project, Tree); end if; Decl_Item := First_Declarative_Item_Of (Top, Tree); while Decl_Item /= Empty_Project_Node loop if Kind_Of (Current_Item_Node (Decl_Item, Tree), Tree) = N_Case_Construction then Case_Construct := Current_Item_Node (Decl_Item, Tree); exit; end if; Decl_Item := Next_Declarative_Item (Decl_Item, Tree); end loop; return Case_Construct; end Find_Case_Statement; ---------------------------------------- -- Move_From_Common_To_Case_Construct -- ---------------------------------------- procedure Move_From_Common_To_Case_Construct (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Node_Id; Pkg : Project_Node_Id; Case_Construct : in out Project_Node_Id; Scenario_Variables : Scenario_Variable_Array; Attribute_Name : GPR.Name_Id; Attribute_Index : GPR.Name_Id := No_Name) is Parent : Project_Node_Id; Node, Tmp : Project_Node_Id; Case_Items : Project_Node_Array_Access := new Project_Node_Array (1 .. 100); Case_Items_Last : Natural := Case_Items'First - 1; procedure Add_Item (Case_Item : Project_Node_Id); -- Add the declaration Node to Case_Item, in front, since the -- declaration necessarily occured before the case item -------------- -- Add_Item -- -------------- procedure Add_Item (Case_Item : Project_Node_Id) is begin Add_Node_To_List (Case_Items, Case_Items_Last, Case_Item); end Add_Item; begin if Pkg /= Empty_Project_Node then Parent := Pkg; else Parent := Project_Declaration_Of (Project, Tree); end if; -- First, create the nested case for the scenario, and memorize each of -- them. This will easily allow to keep the order of all the -- declarations for this attribute that are currently in the common part For_Each_Scenario_Case_Item (Tree, Project, Pkg, Case_Construct, Scenario_Variables, null); For_Each_Matching_Case_Item (Tree, Project, Pkg, Case_Construct, All_Case_Items, Add_Item'Unrestricted_Access); -- Nothing to do if there are no case items if Case_Items_Last > Case_Items'First then Node := First_Declarative_Item_Of (Parent, Tree); while Node /= Empty_Project_Node loop if Attribute_Matches (Tree, Current_Item_Node (Node, Tree), Attribute_Name, Attribute_Index) then for Parent in Case_Items'First .. Case_Items_Last loop Tmp := Default_Project_Node (Tree, N_Declarative_Item); Set_Current_Item_Node (Tmp, Tree, Clone_Node (Tree, Current_Item_Node (Node, Tree), True)); if Kind_Of (Case_Items (Parent), Tree) /= N_Declarative_Item then Set_Next_Declarative_Item (Tmp, Tree, First_Declarative_Item_Of (Case_Items (Parent), Tree)); Set_First_Declarative_Item_Of (Case_Items (Parent), Tree, Tmp); else Set_Next_Declarative_Item (Tmp, Tree, Next_Declarative_Item (Case_Items (Parent), Tree)); Set_Next_Declarative_Item (Case_Items (Parent), Tree, Tmp); end if; Case_Items (Parent) := Tmp; end loop; end if; Node := Next_Declarative_Item (Node, Tree); end loop; Remove_Attribute_Declarations (Tree, Parent, Attribute_Name, Attribute_Index); end if; Free (Case_Items); end Move_From_Common_To_Case_Construct; ---------------------------- -- Internal_Set_Attribute -- ---------------------------- procedure Internal_Set_Attribute (Tree : Project_Tree_Data_Access; Project : Project_Type; Attribute : String; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""; Callback : Set_Attribute_Callback) is Tree_Node : constant GPR.Project_Node_Tree_Ref := GNATCOLL.Projects.Tree (Tree); Sep : constant Natural := Ada.Strings.Fixed.Index (Attribute, "#"); Attribute_Name : constant Name_Id := Get_String (Attribute (Sep + 1 .. Attribute'Last)); Pkg_Name : constant String := Attribute (Attribute'First .. Sep - 1); Pkg_Prj : constant Project_Type := Find_Project_Of_Package (Tree, Project, Pkg_Name); Index_Id : constant Name_Id := Get_String (Index); Pkg : Project_Node_Id := Empty_Project_Node; Case_Construct : Project_Node_Id; procedure Internal_Cb (Case_Item : Project_Node_Id); procedure Internal_Cb (Case_Item : Project_Node_Id) is Previous_Decl : Project_Node_Id; begin Previous_Decl := Find_Last_Declaration_Of (Tree_Node, Case_Item, Attribute_Name, Index_Id); if Previous_Decl /= Empty_Project_Node then Previous_Decl := Expression_Of (Previous_Decl, Tree_Node); end if; Callback (Tree_Node, Pkg_Prj, Attribute_Name, Index_Id, Previous_Decl, Case_Item); end Internal_Cb; begin if not Pkg_Prj.Is_Editable then Trace (Me, "Project is not editable"); else Normalize (Tree, Pkg_Prj); if Pkg_Name /= "" then Pkg := Create_Package (Tree_Node, Pkg_Prj.Node, Pkg_Name); end if; Case_Construct := Find_Case_Statement (Tree_Node, Pkg_Prj.Node, Pkg); Move_From_Common_To_Case_Construct (Tree_Node, Project => Pkg_Prj.Node, Pkg => Pkg, Case_Construct => Case_Construct, Scenario_Variables => Scenario, Attribute_Name => Attribute_Name, Attribute_Index => Index_Id); -- Create the node for the new value For_Each_Scenario_Case_Item (Tree_Node, Pkg_Prj.Node, Pkg, Case_Construct, Scenario, Internal_Cb'Unrestricted_Access); Pkg_Prj.Data.Modified := True; end if; end Internal_Set_Attribute; ------------------- -- Set_Attribute -- ------------------- procedure Set_Attribute (Tree : Project_Tree_Data_Access; Project : Project_Type; Attribute : Attribute_Pkg_String; Value : String; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""; At_Index : Natural := 0) is V : Name_Id; Local_Val : constant String := +Unix_Style_Full_Name (Create (+Value), Cygwin_Style => False); procedure Add_Or_Replace (Tree_Node : GPR.Project_Node_Tree_Ref; Project : Project_Type; Attribute_Name : Name_Id; Index_Id : Name_Id; Previous_Decl : Project_Node_Id; Case_Item : Project_Node_Id); -- Add or replace the attribute Attribute_Name in the declarative list -- for Case_Item procedure Add_Or_Replace (Tree_Node : GPR.Project_Node_Tree_Ref; Project : Project_Type; Attribute_Name : Name_Id; Index_Id : Name_Id; Previous_Decl : Project_Node_Id; Case_Item : Project_Node_Id) is Decl, Val : Project_Node_Id; pragma Unreferenced (Decl, Project); begin Val := Create_Literal_String (V, Tree_Node); if Previous_Decl /= Empty_Project_Node then -- ??? Should we use At_Index here ? Set_Current_Term (First_Term (Previous_Decl, Tree_Node), Tree_Node, Val); Set_Source_Index_Of (Val, Tree_Node, Int (At_Index)); Set_Next_Term (First_Term (Previous_Decl, Tree_Node), Tree_Node, Empty_Project_Node); else Decl := Create_Attribute (Tree_Node, Case_Item, Attribute_Name, Index_Id, GPR.Single, Value => Val, At_Index => At_Index); end if; end Add_Or_Replace; begin if Local_Val = "" then V := Empty_String; else V := Get_String (Local_Val); end if; Internal_Set_Attribute (Tree => Tree, Project => Project, Attribute => String (Attribute), Scenario => Scenario, Index => Index, Callback => Add_Or_Replace'Unrestricted_Access); end Set_Attribute; ------------------- -- Set_Attribute -- ------------------- procedure Set_Attribute (Tree : Project_Tree_Data_Access; Project : Project_Type; Attribute : Attribute_Pkg_List; Values : GNAT.Strings.String_List; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := ""; Prepend : Boolean := False) is List : Project_Node_Id := Empty_Project_Node; function Convert_Dir_Separators (Values : GNAT.Strings.String_List) return GNAT.Strings.String_List; -- Replace all "\" with "/". procedure Add_Or_Replace (Tree_Node : GPR.Project_Node_Tree_Ref; Project : Project_Type; Attribute_Name : Name_Id; Index_Id : Name_Id; Previous_Decl : Project_Node_Id; Case_Item : Project_Node_Id); -- Add or replace the attribute Attribute_Name in the declarative list -- for Case_Item function Convert_Dir_Separators (Values : GNAT.Strings.String_List) return GNAT.Strings.String_List is Result : GNAT.Strings.String_List (Values'Range); begin for A in Values'Range loop if Values (A) /= null then Result (A) := new String' (+Unix_Style_Full_Name (Create (+Values (A).all), Cygwin_Style => False)); end if; end loop; return Result; end Convert_Dir_Separators; Vals : GNAT.Strings.String_List := Convert_Dir_Separators (Values); procedure Add_Or_Replace (Tree_Node : GPR.Project_Node_Tree_Ref; Project : Project_Type; Attribute_Name : Name_Id; Index_Id : Name_Id; Previous_Decl : Project_Node_Id; Case_Item : Project_Node_Id) is Decl, Expr, Term : Project_Node_Id; pragma Unreferenced (Decl); begin if List = Empty_Project_Node then -- Create the string list for the new values. -- This can be prepended later on to the existing list of values. List := Default_Project_Node (Tree_Node, N_Literal_String_List, GPR.List); for A in reverse Vals'Range loop if Vals (A) /= null then Expr := String_As_Expression (Get_String (Vals (A).all), Tree_Node); Set_Next_Expression_In_List (Expr, Tree_Node, First_Expression_In_List (List, Tree_Node)); Set_First_Expression_In_List (List, Tree_Node, Expr); end if; end loop; end if; if Previous_Decl /= Empty_Project_Node then if Prepend then Expr := First_Expression_In_List (List, Tree_Node); while Next_Expression_In_List (Expr, Tree_Node) /= Empty_Project_Node loop Expr := Next_Expression_In_List (Expr, Tree_Node); end loop; Set_Next_Expression_In_List (Expr, Tree_Node, First_Expression_In_List (Current_Term (First_Term (Previous_Decl, Tree_Node), Tree_Node), Tree_Node)); else Set_Next_Expression_In_List (Previous_Decl, Tree_Node, Empty_Project_Node); Set_Next_Term (First_Term (Previous_Decl, Tree_Node), Tree_Node, Empty_Project_Node); end if; Set_Current_Term (First_Term (Previous_Decl, Tree_Node), Tree_Node, List); -- Else create the new instruction to be added to the project else Expr := Enclose_In_Expression (List, Tree_Node); if Prepend then Set_Next_Term (First_Term (Expr, Tree_Node), Tree_Node, Default_Project_Node (Tree_Node, N_Term, GPR.List)); Term := Next_Term (First_Term (Expr, Tree_Node), Tree_Node); Set_Current_Term (Term, Tree_Node, Default_Project_Node (Tree_Node, N_Attribute_Reference, GPR.List)); Term := Current_Term (Term, Tree_Node); Set_Name_Of (Term, Tree_Node, Attribute_Name); Set_Project_Node_Of (Term, Tree_Node, Project.Node); end if; Decl := Create_Attribute (Tree_Node, Case_Item, Attribute_Name, Index_Id, Value => Expr); end if; end Add_Or_Replace; begin Internal_Set_Attribute (Tree => Tree, Project => Project, Attribute => String (Attribute), Scenario => Scenario, Index => Index, Callback => Add_Or_Replace'Unrestricted_Access); Free (Vals); end Set_Attribute; ---------------------- -- Delete_Attribute -- ---------------------- procedure Delete_Attribute (Tree : Project_Tree_Data_Access; Project : Project_Type; Attribute : String; Scenario : Scenario_Variable_Array := All_Scenarios; Index : String := "") is Tree_Node : constant GPR.Project_Node_Tree_Ref := GNATCOLL.Projects.Tree (Tree); Sep : constant Natural := Ada.Strings.Fixed.Index (Attribute, "#"); Attribute_Name : constant Name_Id := Get_String (Attribute (Sep + 1 .. Attribute'Last)); Pkg_Name : constant String := Attribute (Attribute'First .. Sep - 1); Pkg_Prj : constant Project_Type := Find_Project_Of_Package (Tree, Project, Pkg_Name); Pkg : Project_Node_Id := Empty_Project_Node; Case_Construct : Project_Node_Id; Index_Id : constant Name_Id := Get_String (Index); procedure Delete_Attr (Case_Item : Project_Node_Id); -- Remove all definitions for the attribute in the case item ----------------- -- Delete_Attr -- ----------------- procedure Delete_Attr (Case_Item : Project_Node_Id) is begin Remove_Attribute_Declarations (Tree_Node, Case_Item, Attribute_Name, Index_Id); end Delete_Attr; begin if not Pkg_Prj.Is_Editable then Trace (Me, "Project is not editable"); else if Pkg_Name /= "" then Pkg := Find_Package_Declaration (Tree_Node, Pkg_Prj.Node, Get_String (Pkg_Name)); -- If the package doesn't exist, no need to do anything if Pkg = Empty_Project_Node then Trace (Me, "Delete attribute '" & Get_Name_String (Attribute_Name) & "': No such package '" & Pkg_Name & "'"); return; end if; end if; Normalize (Tree, Pkg_Prj); Case_Construct := Find_Case_Statement (Tree_Node, Pkg_Prj.Node, Pkg); Move_From_Common_To_Case_Construct (Tree_Node, Pkg_Prj.Node, Pkg, Case_Construct, Scenario, Attribute_Name, Index_Id); For_Each_Scenario_Case_Item (Tree_Node, Pkg_Prj.Node, Pkg, Case_Construct, Scenario, Delete_Attr'Unrestricted_Access); Pkg_Prj.Data.Modified := True; end if; end Delete_Attribute; ----------------------------- -- For_Each_Directory_Node -- ----------------------------- procedure For_Each_Directory_Node (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Type; Action : Node_Callback) is procedure Process_List (List : Project_Node_Id); -- Process a list of declarative items ------------------ -- Process_List -- ------------------ procedure Process_List (List : Project_Node_Id) is Node : Project_Node_Id := List; Current, Expr, Term, Expr2 : Project_Node_Id; begin while Node /= Empty_Project_Node loop Current := Current_Item_Node (Node, Tree); case Kind_Of (Current, Tree) is when N_Attribute_Declaration => -- ??? Should avoid a hard-coded list of directory -- attributes. Ideally, we would take into account -- the attributes defined in the XML file if GPR.Tree.Name_Of (Current, Tree) = Snames.Name_Source_Dirs or else GPR.Tree.Name_Of (Current, Tree) = Snames.Name_Object_Dir or else GPR.Tree.Name_Of (Current, Tree) = Snames.Name_Exec_Dir then Expr := Expression_Of (Current, Tree); while Expr /= Empty_Project_Node loop Term := First_Term (Expr, Tree); while Term /= Empty_Project_Node loop Current := Current_Term (Term, Tree); case Kind_Of (Current, Tree) is when N_Literal_String_List => Expr2 := First_Expression_In_List (Current, Tree); while Expr2 /= Empty_Project_Node loop Current := Current_Term (First_Term (Expr2, Tree), Tree); if Kind_Of (Current, Tree) /= N_Literal_String or else Next_Term (First_Term (Expr2, Tree), Tree) /= Empty_Project_Node then Trace (Me, "Cannot process lists of " & " non-literal string " & Kind_Of (Current, Tree)'Img); else Action (Current); end if; Expr2 := Next_Expression_In_List (Expr2, Tree); end loop; when N_Literal_String => Action (Current); when others => Trace (Me, "Ignoring " & Kind_Of (Current, Tree)'Img); null; end case; Term := Next_Term (Term, Tree); end loop; Expr := Next_Expression_In_List (Expr, Tree); end loop; end if; when N_Case_Construction => Expr := First_Case_Item_Of (Current, Tree); while Expr /= Empty_Project_Node loop Process_List (First_Declarative_Item_Of (Expr, Tree)); Expr := Next_Case_Item (Expr, Tree); end loop; when others => null; end case; Node := Next_Declarative_Item (Node, Tree); end loop; end Process_List; begin Process_List (First_Declarative_Item_Of (Project_Declaration_Of (Project.Node, Tree), Tree)); end For_Each_Directory_Node; --------------------- -- Rename_And_Move -- --------------------- procedure Rename_And_Move (Tree : Project_Tree_Data_Access; Project : Project_Type; New_Name : String; New_Path : GNATCOLL.VFS.Virtual_File; Errors : Error_Report := null) is Use_Relative_Path : constant Boolean := True; -- Whether to use relative paths when we have to modify with clauses Tree_Node : constant GPR.Project_Node_Tree_Ref := GNATCOLL.Projects.Tree (Tree); Old_Path : constant Filesystem_String := Project.Project_Path.Dir_Name; New_Dir : Virtual_File; procedure Change_Directory (Node : Project_Node_Id); -- Change the directory refered to by Node ---------------------- -- Change_Directory -- ---------------------- procedure Change_Directory (Node : Project_Node_Id) is begin case Kind_Of (Node, Tree_Node) is when N_Literal_String => declare D : constant Filesystem_String := +Get_Name_String (String_Value_Of (Node, Tree_Node)); File : constant Virtual_File := Create (Normalize_Pathname (D, Old_Path, Resolve_Links => False), Get_Host (New_Dir)); begin if not Is_Absolute_Path (D) then Set_String_Value_Of (Node, Tree_Node, Get_String (+Relative_Path (File, New_Dir))); end if; end; when others => Trace (Me, "For_Each_Directory_Node: unknown node type: " & Kind_Of (Node, Tree_Node)'Img); end case; end Change_Directory; Full_Path : Name_Id; Name : constant Name_Id := Get_String (New_Name); Old_Name : constant Name_Id := Get_String (Project.Name); Old : constant Project_Type := Project_Type (Project_From_Name (Tree, Name)); Imported : Project_Type; Iterator : Project_Iterator; With_Clause : Project_Node_Id; Modified : Boolean; P : Path_Name_Type; begin if New_Path = GNATCOLL.VFS.No_File then New_Dir := Create (Old_Path); else New_Dir := New_Path; end if; if Project = No_Project then Trace (Me, "Unspecified project to remove"); return; end if; if Old /= No_Project then Trace (Me, "Rename_And_Move: project " & New_Name & " already exists in the hierarchy"); if Errors /= null then Errors ("Couldn't rename the project to " & New_Name & ASCII.LF & "Project already exists in the project graph"); end if; return; end if; -- Replace all the with_clauses in the project hierarchy that points to -- Project. Iterator := Find_All_Projects_Importing (Project, Direct_Only => False); Full_Path := Get_String (+(Name_As_Directory (Full_Name (New_Dir)) & (+Translate (To_Lower (New_Name), To_Mapping (".", "-"))) & GNATCOLL.Projects.Project_File_Extension)); loop Imported := Current (Iterator); exit when Imported = GNATCOLL.Projects.No_Project; With_Clause := First_With_Clause_Of (Imported.Node, Tree_Node); Modified := False; while With_Clause /= Empty_Project_Node loop if Project_Node_Of (With_Clause, Tree_Node) = Project.Node then Set_Name_Of (With_Clause, Tree_Node, Name); Set_Path_Name_Of (With_Clause, Tree_Node, Path_Name_Of (Project.Node, Tree_Node)); if Use_Relative_Path then Set_String_Value_Of (With_Clause, Tree_Node, Get_String (+Relative_Path (File => Create (+Get_String (Full_Path)), From => Create (Imported.Project_Path.Dir_Name)))); else Set_String_Value_Of (With_Clause, Tree_Node, Full_Path); end if; Modified := True; end if; With_Clause := Next_With_Clause_Of (With_Clause, Tree_Node); end loop; if Modified then Imported.Data.Modified := True; end if; Next (Iterator); end loop; -- If the file was moved, update the source directories so that we still -- point to the same physical directories. if New_Dir.Full_Name /= Old_Path then For_Each_Directory_Node (Tree_Node, Project, Change_Directory'Unrestricted_Access); end if; Set_Name_Of (Project.Node, Tree_Node, Name); Set_Display_Name_Of (Project.Node, Tree_Node, Name); Set_Directory_Of (Project.Node, Tree_Node, Path_Name_Type (Get_String (+New_Dir.Full_Name))); P := Path_Name_Type (Full_Path); Set_Path_Name_Of (Project.Node, Tree_Node, P); if Get_View (Project) /= GPR.No_Project then Get_View (Project).Path := (Name => P, Display_Name => P); end if; -- We do not want to reread the display_name from the source, which is -- no longer up-to-date, so we'll force its refresh from the tree Set_Location_Of (Project.Node, Tree_Node, No_Location); -- Unregister the old name GPR.Tree_Private_Part.Projects_Htable.Set (Tree_Node.Projects_HT, GPR.Tree.Name_Of (Project.Node, Tree_Node), GPR.Tree_Private_Part.Project_Name_And_Node' (Name => Old_Name, Node => Empty_Project_Node, Resolved_Path => Path_Name_Type (Old_Name), Extended => False, From_Extended => False, Proj_Qualifier => Unspecified)); -- Register the new name GPR.Tree_Private_Part.Projects_Htable.Set (Tree_Node.Projects_HT, GPR.Tree.Name_Of (Project.Node, Tree_Node), GPR.Tree_Private_Part.Project_Name_And_Node' (Name => Name, Resolved_Path => Path_Name_Type (Full_Path), Node => Project.Node, Extended => False, From_Extended => False, Proj_Qualifier => Unspecified)); Project.Data.Modified := True; Unchecked_Free (Project.Data.Imported_Projects.Items); Project.Data.Imported_Projects.Last := 0; Unchecked_Free (Project.Data.Importing_Projects); exception when E : others => Trace (Exception_Handle, E); end Rename_And_Move; --------------------- -- Normalize_Cases -- --------------------- procedure Normalize_Cases (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Type) is procedure Process_Declarative_List (Node : Project_Node_Id); -- Check all case statements in the declarative list ------------------------------ -- Process_Declarative_List -- ------------------------------ procedure Process_Declarative_List (Node : Project_Node_Id) is Decl_Item, Current : Project_Node_Id := Node; When_Others_Node : Project_Node_Id := Empty_Project_Node; begin -- Nothing to do if there is no project if Node = Empty_Project_Node then return; end if; pragma Assert (Kind_Of (Decl_Item, Tree) = N_Declarative_Item); while Decl_Item /= Empty_Project_Node loop Current := Current_Item_Node (Decl_Item, Tree); exit when Current = Empty_Project_Node; case Kind_Of (Current, Tree) is when N_Package_Declaration => Process_Declarative_List (First_Declarative_Item_Of (Current, Tree)); when N_Case_Construction => declare Values : Name_Id_Array := Get_All_Possible_Values (Tree, Case_Variable_Reference_Of (Current, Tree)); Case_Item : Project_Node_Id := First_Case_Item_Of (Current, Tree); Choice : Project_Node_Id; When_Others_Present : Boolean := False; begin while Case_Item /= Empty_Project_Node loop Choice := First_Choice_Of (Case_Item, Tree); if Choice = Empty_Project_Node then -- "when othes" choice. When_Others_Present := True; end if; while Choice /= Empty_Project_Node loop for N in Values'Range loop if Values (N) = String_Value_Of (Choice, Tree) then Values (N) := No_Name; exit; end if; end loop; Choice := Next_Literal_String (Choice, Tree); end loop; Process_Declarative_List (First_Declarative_Item_Of (Case_Item, Tree)); if When_Others_Present then When_Others_Node := First_Declarative_Item_Of (Case_Item, Tree); end if; Case_Item := Next_Case_Item (Case_Item, Tree); end loop; for V in Values'Range loop if Values (V) /= No_Name then Add_Case_Item (Tree => Tree, Case_Node => Current, Choice => Values (V), Decl => When_Others_Node); end if; end loop; end; when others => null; end case; Decl_Item := Next_Declarative_Item (Decl_Item, Tree); end loop; end Process_Declarative_List; begin Process_Declarative_List (First_Declarative_Item_Of (Project_Declaration_Of (Project.Node, Tree), Tree)); end Normalize_Cases; -------------------------- -- Set_With_Clause_Path -- -------------------------- procedure Set_With_Clause_Path (Tree : GPR.Project_Node_Tree_Ref; With_Clause : Project_Node_Id; Imported_Project_Location : Virtual_File; Imported_Project : Project_Node_Id; Importing_Project : Project_Node_Id; Use_Relative_Path : Boolean; Use_Base_Name : Boolean; Limited_With : Boolean := False) is Clause : Name_Id; begin if Use_Base_Name then Clause := Get_String (+Base_Name (Imported_Project_Location)); elsif Use_Relative_Path then Clause := Get_String (+Imported_Project_Location.Relative_Path (Create (GNATCOLL.VFS_Utils.Dir_Name (+Get_String (Name_Id (Path_Name_Of (Importing_Project, Tree))))))); else Clause := Get_String (+Imported_Project_Location.Full_Name); end if; Set_String_Value_Of (With_Clause, Tree, Clause); Set_Path_Name_Of (With_Clause, Tree, GPR.Tree.Path_Name_Of (Imported_Project, Tree)); Set_Project_Node_Of (With_Clause, Tree, Imported_Project, Limited_With => Limited_With); end Set_With_Clause_Path; -------------------------- -- Add_Imported_Project -- -------------------------- function Add_Imported_Project (Tree : Project_Tree_Data_Access; Project : Project_Type'Class; Imported_Project : Project_Type'Class; Errors : Error_Report := null; Use_Relative_Path : Boolean; Use_Base_Name : Boolean; Limited_With : Boolean := False) return Import_Project_Error is Tree_Node : constant GPR.Project_Node_Tree_Ref := GNATCOLL.Projects.Tree (Tree); procedure Fail (S : String); -- Replaces Osint.Fail ---------- -- Fail -- ---------- procedure Fail (S : String) is begin if Errors /= null then Errors (S); end if; end Fail; With_Clause : Project_Node_Id; Imported_Name : constant Name_Id := GPR.Tree.Name_Of (Imported_Project.Node, Tree_Node); begin Output.Set_Special_Output (Output.Output_Proc (Errors)); GPR.Com.Fail := Fail'Unrestricted_Access; -- Make sure we are not trying to import ourselves, since otherwise it -- would result in an infinite loop when manipulating the project. if GPR.Tree.Name_Of (Project.Data.Node, Tree_Node) = Imported_Name then Fail ("Cannot add dependency to self"); Output.Cancel_Special_Output; GPR.Com.Fail := null; return Dependency_On_Self; end if; -- Check if it is already there. If we have the same name but not the -- same path, we replace it anyway. With_Clause := First_With_Clause_Of (Project.Node, Tree_Node); while With_Clause /= Empty_Project_Node loop if GPR.Tree.Name_Of (Project_Node_Of (With_Clause, Tree_Node), Tree_Node) = Imported_Name then Fail ("There is already a dependency on " & Imported_Project.Name); Output.Cancel_Special_Output; GPR.Com.Fail := null; return Dependency_Already_Exists; end if; With_Clause := Next_With_Clause_Of (With_Clause, Tree_Node); end loop; -- Would we introduce a circular reference by adding this project ? if Project.Data.Importing_Projects /= null then for P in Project.Data.Importing_Projects'Range loop if To_Lower (Project_From_Path (Tree, Project.Data.Importing_Projects (P)).Name) = To_Lower (Get_Name_String (Imported_Name)) then Fail ("Circular dependency detected in the project hierarchy"); Output.Cancel_Special_Output; GPR.Com.Fail := null; return Circular_Dependency; end if; end loop; end if; -- Edit the project With_Clause := Default_Project_Node (Tree_Node, N_With_Clause); Set_Name_Of (With_Clause, Tree_Node, Imported_Name); Set_Next_With_Clause_Of (With_Clause, Tree_Node, First_With_Clause_Of (Project.Node, Tree_Node)); Set_First_With_Clause_Of (Project.Node, Tree_Node, With_Clause); Set_With_Clause_Path (Tree_Node, With_Clause, Imported_Project.Project_Path, Imported_Project.Node, Project.Node, Use_Relative_Path => Use_Relative_Path, Use_Base_Name => Use_Base_Name, Limited_With => Limited_With); Output.Cancel_Special_Output; GPR.Com.Fail := null; Project.Data.Modified := True; Reset_All_Caches (Tree); return Success; exception when others => Output.Cancel_Special_Output; GPR.Com.Fail := null; raise; end Add_Imported_Project; ------------------------------ -- Delete_Scenario_Variable -- ------------------------------ procedure Delete_Scenario_Variable (Tree : Project_Tree_Data_Access; Root_Project : Project_Type; External_Name : String; Keep_Choice : String; Delete_Direct_References : Boolean := True) is Tree_Node : constant GPR.Project_Node_Tree_Ref := GNATCOLL.Projects.Tree (Tree); procedure Callback (Project, Parent, Node, Choice : Project_Node_Id); -- Called for each mtching node for the env. variable -------------- -- Callback -- -------------- procedure Callback (Project, Parent, Node, Choice : Project_Node_Id) is pragma Unreferenced (Choice); begin case Kind_Of (Node, Tree_Node) is when N_External_Value => if Delete_Direct_References then Set_Current_Term (Parent, Tree_Node, Create_Literal_String (Get_String (Keep_Choice), Tree_Node)); end if; when N_Variable_Reference => Set_Current_Term (Parent, Tree_Node, Create_Literal_String (Get_String (Keep_Choice), Tree_Node)); when N_Typed_Variable_Declaration => Remove_Node (Tree_Node, Project, Node); Remove_Variable_Declaration (Tree_Node, Project, Node); when N_String_Type_Declaration => Remove_Node (Tree_Node, Project, Node); when N_Case_Item => -- The first declarative item might be null when there was no -- actual "when ..." for Keep_Choice. In that case, GPR.Proc -- inserts an entry with no declarative item. if First_Declarative_Item_Of (Node, Tree_Node) /= Empty_Project_Node then Tree_Node.Project_Nodes.Table (Parent) := Tree_Node.Project_Nodes.Table (First_Declarative_Item_Of (Node, Tree_Node)); else Set_Current_Item_Node (Parent, Tree_Node, Empty_Project_Node); end if; when others => null; pragma Assert (False, "Unexpected node type"); end case; end Callback; begin Normalize (Tree, Root_Project); For_Each_Environment_Variable (Tree_Node, Root_Project, Get_String (External_Name), Get_String (Keep_Choice), Callback'Unrestricted_Access); end Delete_Scenario_Variable; ----------------- -- Rename_Path -- ----------------- function Rename_Path (Tree : Project_Tree_Data_Access; Project : Project_Type; Old_Path : Virtual_File; New_Path : Virtual_File; Use_Relative_Paths : Boolean) return Boolean is Tree_Node : constant GPR.Project_Node_Tree_Ref := GNATCOLL.Projects.Tree (Tree); Changed : Boolean := False; procedure Rename_P (Node : Project_Node_Id); -- Convert the path to an absolute path function Path (P : Filesystem_String) return Filesystem_String; -- Returns the path with relative or absolute convention ---------- -- Path -- ---------- function Path (P : Filesystem_String) return Filesystem_String is begin -- We use to test whether P is an url, but in the context of projects -- we only have directories anyway if Use_Relative_Paths and then Is_Absolute_Path (P) then declare File : constant Virtual_File := Create (P, Project_Path (Project).Get_Host); Conv : constant Filesystem_String := Relative_Path (File, Dir (Project_Path (Project))); begin return Conv; end; elsif not Use_Relative_Paths then declare Conv : constant Filesystem_String := Normalize_Pathname (P, Dir (Project_Path (Project)).Full_Name); begin return Conv; end; end if; return P; end Path; Old_P : constant Filesystem_String := Path (Old_Path.Full_Name); New_P : constant Filesystem_String := Path (New_Path.Full_Name); -------------- -- Rename_P -- -------------- procedure Rename_P (Node : Project_Node_Id) is Node_P : constant Filesystem_String := Path (+Get_String (String_Value_Of (Node, Tree_Node))); begin -- ??? We used to test File_Equal on the Build_Server, but there is -- no such notion in GNATCOLL if Node_P'Length >= Old_P'Length and then File_Equal (Node_P (Node_P'First .. Node_P'First + Old_P'Length - 1), Old_P, Local_Host) then Set_String_Value_Of (Node, Tree_Node, Get_String (+New_P & (+Node_P (Node_P'First + Old_P'Length .. Node_P'Last)))); Changed := True; end if; end Rename_P; begin -- Replace all the paths For_Each_Directory_Node (Tree_Node, Project, Rename_P'Unrestricted_Access); if Changed then Project.Set_Modified (True); end if; return Changed; end Rename_Path; ----------------- -- Create_Type -- ----------------- function Create_Type (Tree : GPR.Project_Node_Tree_Ref; Prj_Or_Pkg : Project_Node_Id; Name : String) return Project_Node_Id is Node : Project_Node_Id; begin Node := Default_Project_Node (Tree, N_String_Type_Declaration); Set_Name_Of (Node, Tree, Get_String (Name)); Add_At_End (Tree, Prj_Or_Pkg, Node, True, True); return Node; end Create_Type; ------------------------ -- Add_Possible_Value -- ------------------------ procedure Add_Possible_Value (Tree : GPR.Project_Node_Tree_Ref; Typ : Project_Node_Id; Choice : String) is C : constant Name_Id := Get_String (Choice); Str, S2 : Project_Node_Id; begin pragma Assert (Kind_Of (Typ, Tree) = N_String_Type_Declaration); Str := First_Literal_String (Typ, Tree); while Str /= Empty_Project_Node loop if String_Value_Of (Str, Tree) = C then return; end if; Str := Next_Literal_String (Str, Tree); end loop; S2 := Create_Literal_String (C, Tree); Set_Next_Literal_String (S2, Tree, First_Literal_String (Typ, Tree)); Set_First_Literal_String (Typ, Tree, S2); end Add_Possible_Value; -------------------------- -- Expression_As_String -- -------------------------- function Expression_As_String (Tree : GPR.Project_Node_Tree_Ref; Expression : Project_Node_Id) return Name_Id is Term : Project_Node_Id; begin case Kind_Of (Expression, Tree) is when N_Literal_String => return String_Value_Of (Expression, Tree); when N_Expression => Term := First_Term (Expression, Tree); if Term /= Empty_Project_Node and then Next_Term (Term, Tree) = Empty_Project_Node and then Kind_Of (Current_Term (Term, Tree), Tree) = N_Literal_String then return String_Value_Of (Current_Term (Term, Tree), Tree); else return No_Name; end if; when others => return No_Name; end case; end Expression_As_String; ---------------------------- -- Find_Scenario_Variable -- ---------------------------- function Find_Scenario_Variable (Tree : GPR.Project_Node_Tree_Ref; Project : Project_Type; External_Name : String) return Project_Node_Id is Decl : Project_Node_Id := First_Declarative_Item_Of (Project_Declaration_Of (Project.Node, Tree), Tree); Current : Project_Node_Id; begin while Decl /= Empty_Project_Node loop Current := Current_Item_Node (Decl, Tree); if Kind_Of (Current, Tree) = N_Typed_Variable_Declaration and then Is_External_Variable (Current, Tree) then Get_Name_String (External_Reference_Of (Current, Tree)); if Name_Buffer (1 .. Name_Len) = External_Name then return Current; end if; end if; Decl := Next_Declarative_Item (Decl, Tree); end loop; return Empty_Project_Node; end Find_Scenario_Variable; -------------------------- -- Is_Virtual_Extending -- -------------------------- function Is_Virtual_Extending (Tree : GPR.Project_Node_Tree_Ref; Node : GPR.Project_Node_Id) return Boolean is Name : constant String := Get_String (GPR.Tree.Name_Of (Node, Tree)); begin return Name'Length > Virtual_Prefix'Length and then Name (1 .. Virtual_Prefix'Length) = Virtual_Prefix; end Is_Virtual_Extending; end GNATCOLL.Projects.Normalize; gnatcoll-core-21.0.0/src/gnatcoll-email-parser.adb0000644000175000017500000003171113661715457021630 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ pragma Warnings (Off, "*internal GNAT unit*"); with Ada.Strings.Unbounded.Aux; pragma Warnings (On, "*internal GNAT unit*"); with GNAT.Case_Util; use GNAT.Case_Util; with GNATCOLL.VFS; use GNATCOLL.VFS; with GNAT.Strings; use GNAT.Strings; package body GNATCOLL.Email.Parser is function Preserve_Header (Name : String) return Boolean; pragma Inline (Preserve_Header); -- Whether the given header should be preserved in the generated message procedure Parse_Payload (Msg : in out Message; Unparsed : String); -- Parse the payload, as read in Unparsed, into its various components, -- and store them in the message appropriately --------------------- -- Preserve_Header -- --------------------- function Preserve_Header (Name : String) return Boolean is N : String := Name; begin To_Lower (N); case N (N'First) is when 'c' => return N = "cc" or else N = "content-type"; when 'd' => return N = "date"; when 'f' => return N = "from"; when 'i' => return N = "in-reply-to"; when 'm' => return N = "message-id" or else N = "mime-version"; when 'r' => return N = "references" or else N = "reply-to"; when 's' => return N = "subject"; when 't' => return N = "to"; when 'x' => return True; -- All X-* headers when others => return False; end case; end Preserve_Header; ----------- -- Parse -- ----------- procedure Parse (Str : String; Msg : out Message) is begin Full_Parse (Str, Msg, Store_Headers => True, Store_Payload => True, Parse_Payload => True); end Parse; -------------------------- -- Parse_Ignore_Headers -- -------------------------- procedure Parse_Ignore_Headers (Str : String; Msg : out Message) is begin Full_Parse (Str, Msg, Store_Headers => False, Store_Payload => True, Parse_Payload => False); end Parse_Ignore_Headers; --------------------------- -- Parse_Minimal_Headers -- --------------------------- procedure Parse_Minimal_Headers (Str : String; Msg : out Message) is begin Full_Parse (Str, Msg, Store_Headers => True, Store_Payload => True, Parse_Payload => True, Filter => Preserve_Header'Access); end Parse_Minimal_Headers; ---------------------- -- Parse_No_Payload -- ---------------------- procedure Parse_No_Payload (Str : String; Msg : out Message) is begin Full_Parse (Str, Msg, Store_Headers => True, Store_Payload => True, Parse_Payload => False); end Parse_No_Payload; -------------------------------------- -- Parse_No_Payload_Minimal_Headers -- -------------------------------------- procedure Parse_No_Payload_Minimal_Headers (Str : String; Msg : out Message) is begin Full_Parse (Str, Msg, Store_Headers => True, Store_Payload => True, Parse_Payload => False, Filter => Preserve_Header'Access); end Parse_No_Payload_Minimal_Headers; ---------------- -- Full_Parse -- ---------------- procedure Full_Parse (Str : String; Msg : out Message; Store_Headers : Boolean := True; Store_Payload : Boolean := True; Parse_Payload : Boolean := True; Filter : Header_Filter := null) is Index : Integer := Str'First; Stop : constant Integer := Str'Last; Colon : Integer; Eol : Integer; Next, Eol2 : Integer; Is_Continuation : Boolean; Value : Unbounded_String; function RTrim_CR (Item : String) return String is (if Item /= "" and then Item (Item'Last) = ASCII.CR then Item (Item'First .. Item'Last - 1) else Item); function LTrim_Space (Item : String) return String is (if Item /= "" and then Item (Item'First) = ' ' then Item (Item'First + 1 .. Item'Last) else Item); begin Msg := New_Message (MIME_Type => ""); -- Do we have an envelope for the message ? if Index + 4 < Str'Last and then Str (Index .. Index + 4) = "From " then Eol := Next_Occurrence (Str (Index .. Stop), ASCII.LF); Set_Envelope_From (Msg, Str (Index .. Eol - 1)); Index := Eol + 1; end if; -- Find the headers block. It is defined as being the set of lines up -- to the first line that doesn't match the headers format. This can be -- an empty line (and should generally be the case according to -- RFC2822), but could be anything else, in which case the extra line -- is assumed to belong to the body while Index <= Stop loop Eol := Next_Occurrence (Str (Index .. Stop), ASCII.LF); Colon := Next_Occurrence (Str (Index .. Eol), ':'); exit when Colon > Eol; -- ??? Header names are characters between 33 and 126 inclusive. We -- should check -- Check for continuation lines: if the next line starts with a -- whitespace but contains other characters than whitespaces, it is -- part of the same header. We have this whitespace handling because -- of cases where the subject line is followed by the separator line, -- itself starting with a space. This is not full RFC2822 of course, -- but it is nice to handle this correctly anyway Value := To_Unbounded_String (LTrim_Space (RTrim_CR (Str (Colon + 1 .. Eol - 1)))); while Eol < Str'Last and then Is_Whitespace (Str (Eol + 1)) loop Next := Eol + 1; Is_Continuation := False; Eol2 := Next_Occurrence (Str (Next .. Stop), ASCII.LF); for F in Next + 1 .. Eol2 - 1 loop if not Is_Whitespace (Str (F)) then Append (Value, ' ' & RTrim_CR (Str (F .. Eol2 - 1))); Is_Continuation := True; exit; end if; end loop; exit when not Is_Continuation; Eol := Eol2; end loop; if Store_Headers and then (Filter = null or else Filter (Str (Index .. Colon - 1))) then Add_Header (Msg, Create (Name => Str (Index .. Colon - 1), Value => To_String (Value))); end if; Index := Eol + 1; end loop; -- A blank line is not part of the body, any other line is if Index <= Str'Last and then Str (Index) = ASCII.LF then Index := Index + 1; end if; if Store_Payload then if not Parse_Payload then -- Note: do not use Set_Text_Payload here, as this would reset -- the Content-Type header. Msg.Contents.Payload := (Multipart => False, Text => To_Unbounded_String (Str (Index .. Str'Last))); else Email.Parser.Parse_Payload (Msg, Str (Index .. Str'Last)); end if; end if; exception when others => Msg := Null_Message; end Full_Parse; ------------------- -- Parse_Payload -- ------------------- procedure Parse_Payload (Msg : in out Message; Unparsed : String) is Boundary : constant String := Get_Boundary (Msg); Length : constant Natural := Boundary'Length; Index : Integer := Unparsed'First; Tmp : Integer; Is_Last_Boundary : Boolean := False; Is_Boundary : Boolean; Start : Integer := -1; Attachment : Message; begin if Boundary = "" then Set_Text_Payload (Msg, Unparsed, MIME_Type => ""); else while not Is_Last_Boundary and then Index + Length < Unparsed'Last loop if Unparsed (Index) = '-' and then Unparsed (Index + 1) = '-' and then Unparsed (Index + 2 .. Index + 1 + Length) = Boundary then Tmp := Index + 2 + Length; if Unparsed (Tmp) = '-' and then Unparsed (Tmp + 1) = '-' then Is_Last_Boundary := True; Tmp := Tmp + 2; end if; Is_Boundary := True; while Tmp <= Unparsed'Last and then Unparsed (Tmp) /= ASCII.LF loop if not Is_Whitespace (Unparsed (Tmp)) then -- Not a boundary after all Is_Boundary := False; Is_Last_Boundary := False; exit; end if; Tmp := Tmp + 1; end loop; if Is_Boundary then if Start /= -1 then Full_Parse (Str => Unparsed (Start .. Index - 2), Msg => Attachment, Store_Headers => True, Store_Payload => True, Parse_Payload => True); if Attachment /= Null_Message then Add_Payload (Msg, Attachment); else -- Should exit with error message I guess null; end if; else Set_Preamble (Msg, Unparsed (Unparsed'First .. Index - 2)); end if; Start := Tmp + 1; Is_Last_Boundary := Is_Last_Boundary or else Tmp + Length >= Unparsed'Last; end if; Index := Next_Occurrence (Unparsed (Tmp .. Unparsed'Last), ASCII.LF) + 1; else Index := Next_Occurrence (Unparsed (Index .. Unparsed'Last), ASCII.LF) + 1; end if; end loop; end if; if Index < Unparsed'Last and then Start /= -1 then Set_Epilogue (Msg, Unparsed (Start .. Unparsed'Last)); end if; end Parse_Payload; -------------------------- -- Full_Parse_From_File -- -------------------------- procedure Full_Parse_From_File (Filename : Virtual_File; Msg : out Message; Store_Headers : Boolean := True; Store_Payload : Boolean := True; Parse_Payload : Boolean := True; Filter : Header_Filter := null) is Str : GNAT.Strings.String_Access; begin Str := Read_File (Filename); Full_Parse (Str.all, Msg, Store_Headers, Store_Payload, Parse_Payload, Filter); Free (Str); end Full_Parse_From_File; ------------------- -- Parse_Payload -- ------------------- procedure Parse_Payload (Msg : in out Message) is use Ada.Strings.Unbounded.Aux; Payload : constant Unbounded_String := Msg.Contents.Payload.Text; Payload_Str : Big_String_Access; Payload_Len : Natural; begin Msg.Contents.Payload.Text := Null_Unbounded_String; Get_String (Payload, Payload_Str, Payload_Len); Parse_Payload (Msg, Payload_Str (1 .. Payload_Len)); end Parse_Payload; end GNATCOLL.Email.Parser; gnatcoll-core-21.0.0/src/paragraph_filling/0000755000175000017500000000000013661715457020444 5ustar nicolasnicolasgnatcoll-core-21.0.0/src/paragraph_filling/gnatcoll-paragraph_filling-words.ads0000644000175000017500000001016713661715457027550 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This software was originally contributed by William A. Duff with Ada.Containers.Vectors; with Ada.Strings.Unbounded; private package GNATCOLL.Paragraph_Filling.Words is -- Provides ways of differentiating words by reformatting a paragraph and -- pointing to the first character of each word in that paragraph. type Word_Index is new Positive; package Word_Vectors is new Ada.Containers.Vectors ( Index_Type => Word_Index, Element_Type => Word_Index); use Word_Vectors; subtype Word_Vector is Word_Vectors.Vector; subtype Word_Count is Word_Index'Base range 0 .. Word_Index'Last; type Words (After_Last_Word : Word_Count) is limited private; function Index_Paragraph (Paragraph : String) return Words; -- Creates a record with an array of the indexes to the first character of -- each word in Paragraph, plus an index pointing one past the end of the -- Paragraph. function Nth_Word (W : Words; N : Word_Index) return String; -- Returns the Nth word in W.PAragraph function Last_Word (W : Words) return Word_Count; -- Returns the word number of the last word (in other words the number of -- words) in W.Paragraph. function Word_Length (W : Words; N : Word_Index) return Positive; pragma Inline (Word_Length); -- Returns the length of the Nth word in W.Paragraph function Line_Length (W : Words; X, Y : Word_Index) return Positive; -- Returns the length of a line beginning with the Xth word and ending with -- the Yth word. function Merge_Lines (W : Words; Split_Before_Word : Word_Vector; Line_Prefix : String := "") return Ada.Strings.Unbounded.Unbounded_String; -- Return the formatted paragraph, by splitting lines before each word -- in Split_Before_Word. -- Each created line will start with Line_Prefix. private type Word_Starts is array (Word_Index range <>) of Word_Index; type Words (After_Last_Word : Word_Count) is limited record Paragraph : Ada.Strings.Unbounded.Unbounded_String; Starts : Word_Starts (1 .. After_Last_Word); end record; -- Paragraph is the actual text of the Words. Starts contains indexes to -- the first character of each word in Paragraph. This facilitates the -- formatting algorithms in Paragraph_Filling because some only need to -- know the word lengths and positions most of the time. end GNATCOLL.Paragraph_Filling.Words; gnatcoll-core-21.0.0/src/paragraph_filling/gnatcoll-paragraph_filling-badnesses.adb0000644000175000017500000001061713661715457030340 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This software was originally contributed by William A. Duff package body GNATCOLL.Paragraph_Filling.Badnesses is ---------- -- "**" -- ---------- function "**" (X : Natural; Y : Positive) return Badness_Value is pragma Unsuppress (All_Checks); begin return Badness_Value (Integer'(X ** Y)); exception when Constraint_Error => return Infinity; end "**"; --------- -- "+" -- --------- overriding function "+" (X, Y : Badness_Value) return Badness_Value is pragma Unsuppress (All_Checks); begin return Badness_Value (Integer (X) + Integer (Y)); exception when Constraint_Error => return Infinity; end "+"; --------- -- "<" -- --------- overriding function "<" (X, Y : Badness_Value) return Boolean is begin return Integer (X) < Integer (Y); end "<"; ----------- -- Image -- ----------- function Image (Badness : Badness_Value) return String is begin if Badness = Infinity then return "Inf"; else declare Result : constant String := Badness_Value'Image (Badness); begin -- Slicing removes the superfluous space return Result (Result'First + 1 .. Result'Last); end; end if; end Image; ------------------ -- Line_Badness -- ------------------ function Line_Badness (W : Paragraph_Filling.Words.Words; X, Y : Word_Index; Max_Line_Length : Positive; Format_Last_Line : Boolean := False) return Badness_Value is Distance : constant Integer := Max_Line_Length - Line_Length (W, X, Y); begin -- Line is too long if Distance < 0 then -- One word line, meaning nothing can be done to shorten it if X = Y then return 0; -- Not one word line, meaning it can be split into two pieces else return Infinity; end if; -- Last line is not bad if unless Format_Last_Line = True elsif Y = Last_Word (W) and then not Format_Last_Line then return 0; -- Otherwise, normal line. Return the badness of distance to the end -- of the line squared. else return Distance ** 2; end if; end Line_Badness; ------------------ -- Line_Badness -- ------------------ function Line_Badness (Line_Length : Positive; Max_Line_Length : Positive) return Badness_Value is begin if Line_Length > Max_Line_Length then return Infinity; else return (Max_Line_Length - Line_Length) ** 2; end if; end Line_Badness; end GNATCOLL.Paragraph_Filling.Badnesses; gnatcoll-core-21.0.0/src/paragraph_filling/gnatcoll-paragraph_filling.ads0000644000175000017500000001162413661715457026413 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2011-2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This software was originally contributed by William A. Duff with Ada.Strings.Unbounded; package GNATCOLL.Paragraph_Filling is -- The purpose of this package is to format paragraphs to take up the -- minimal number of lines and to look better. -- Note: All subprograms in this package that take or return a String -- representing a paragraph represent multiple lines by using ASCII.LF -- as the line terminator. -- They return an unbounded_string to avoid extra copies (since internally -- they manipulate an unbounded_string). Default_Max_Line_Length : Positive := 79; -- This value is used as a default for the Max_Line_Length parameter of -- various subprograms. Note that 79 is the standard max line length used -- at AdaCore. function Greedy_Fill (Paragraph : String; Max_Line_Length : Positive := Default_Max_Line_Length; Line_Prefix : String := "") return Ada.Strings.Unbounded.Unbounded_String; -- Formats a paragraph with the greedy algorithm (by putting as many words -- as possible on each line). -- Line_Prefix is added at the beginning of each line. function Pretty_Fill (Paragraph : String; Max_Line_Length : Positive := Default_Max_Line_Length) return Ada.Strings.Unbounded.Unbounded_String; -- Formats a paragraph by first performing Greedy_Fill and then comparing -- adjacent lines and deciding whether a word should be moved to the next -- line to make the lines more even. For example: -- -- Reads Ada source code from the file named by Input_Name. Calls Format on -- each block comment, and sends the output to the file named by -- Output_Name. Text that is not part of a comment, and comments appearing -- after other non-whitespace text on the same line, is sent to the output -- unchanged. -- -- would be changed to: -- -- Reads Ada source code from the file named by Input_Name. Calls Format -- on each block comment, and sends the output to the file named by -- Output_Name. Text that is not part of a comment, and comments appearing -- after other non-whitespace text on the same line, is sent to the output -- unchanged. -- -- if the max line length is set to 72. function Knuth_Fill (Paragraph : String; Max_Line_Length : Positive := Default_Max_Line_Length; Line_Prefix : String := "") return Ada.Strings.Unbounded.Unbounded_String; -- Fill the paragraph in the best possible way, based on an algorithm -- invented by Knuth. This algorithm uses dynamic programming techniques in -- order to fill paragraphs so that they have the lowest possible badness -- and line count. Badness is calculated by the Line_Badness function in -- Paragraph_Filling.Badnesses. For details see the paper, "Breaking -- Paragraphs into Lines", by Donald E. Knuth and Michael F. Plass, -- Software Practice and Experience, 11 (1981). function No_Fill (Paragraph : String; Max_Line_Length : Positive := Default_Max_Line_Length; Line_Prefix : String := "") return Ada.Strings.Unbounded.Unbounded_String; -- Return Paragraph unchanged end GNATCOLL.Paragraph_Filling; gnatcoll-core-21.0.0/src/paragraph_filling/gnatcoll-paragraph_filling-words.adb0000644000175000017500000001417413661715457027531 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Containers; use Ada.Containers; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with GNATCOLL.Utils; use GNATCOLL.Utils; package body GNATCOLL.Paragraph_Filling.Words is ----------------- -- Merge_Lines -- ----------------- function Merge_Lines (W : Words; Split_Before_Word : Word_Vector; Line_Prefix : String := "") return Ada.Strings.Unbounded.Unbounded_String is Result : Unbounded_String; From : Integer := 1; Start : Integer; To : Integer; Before_Word : Word_Index; begin for Count in reverse 2 .. Length (Split_Before_Word) - 1 loop Before_Word := Element (Split_Before_Word, Word_Index (Count)); Start := Integer (W.Starts (Before_Word)); To := Start - 1; while Is_Whitespace (Element (W.Paragraph, To)) loop To := To - 1; end loop; Append (Result, Line_Prefix); Append (Result, Slice (W.Paragraph, From, To)); Append (Result, ASCII.LF); From := Start; end loop; To := Length (W.Paragraph); while Is_Whitespace (Element (W.Paragraph, To)) loop To := To - 1; end loop; Append (Result, Line_Prefix); Append (Result, Slice (W.Paragraph, From, To)); Append (Result, ASCII.LF); return Result; end Merge_Lines; --------------------- -- Index_Paragraph -- --------------------- function Index_Paragraph (Paragraph : String) return Words is Fixed_Para : Unbounded_String; Count : Positive := Paragraph'First; Result : Word_Vector; begin Append (Result, 1); -- First word always starts on first character -- Takes out all spaces, tabs, and new line characters and creates a -- string with exactly one space between each word, plus one space at -- the end. if Paragraph /= "" then -- Skip leading whitespaces while Is_Whitespace (Paragraph (Count)) loop Count := Count + 1; end loop; while Count <= Paragraph'Last loop if Is_Whitespace (Paragraph (Count)) then loop Count := Count + 1; exit when Count > Paragraph'Last or else not Is_Whitespace (Paragraph (Count)); end loop; Append (Fixed_Para, ' '); Append (Result, Word_Index (Length (Fixed_Para) + 1)); if Count <= Paragraph'Last then Append (Fixed_Para, Paragraph (Count)); end if; else -- ??? Might be more efficient to find the longuest substring -- with no multiple-space sequence, and append it at once. If -- the paragraph is already correct, we avoid a whole copy. Append (Fixed_Para, Paragraph (Count)); end if; Count := Count + 1; end loop; if Element (Fixed_Para, Length (Fixed_Para)) /= ' ' then Append (Fixed_Para, ' '); end if; end if; -- Avoid extra copies of Starts array by building the result in place return W : Words (After_Last_Word => Word_Index (Length (Result))) do W.Paragraph := Fixed_Para; for Count in 1 .. Length (Result) loop W.Starts (Word_Index (Count)) := Word_Vectors.Element (Result, Word_Index (Count)); end loop; end return; end Index_Paragraph; --------------- -- Last_Word -- --------------- function Last_Word (W : Words) return Word_Count is begin return W.After_Last_Word - 1; end Last_Word; ----------------- -- Line_Length -- ----------------- function Line_Length (W : Words; X, Y : Word_Index) return Positive is begin return Positive (W.Starts (Y + 1) - W.Starts (X) - 1); end Line_Length; -------------- -- Nth_Word -- -------------- function Nth_Word (W : Words; N : Word_Index) return String is begin return Slice (W.Paragraph, Low => Integer (W.Starts (N)), High => Integer (W.Starts (N + 1) - 2)); end Nth_Word; ----------------- -- Word_Length -- ----------------- function Word_Length (W : Words; N : Word_Index) return Positive is begin return Integer (W.Starts (N + 1) - W.Starts (N) - 1); end Word_Length; end GNATCOLL.Paragraph_Filling.Words; gnatcoll-core-21.0.0/src/paragraph_filling/gnatcoll-paragraph_filling.adb0000644000175000017500000003723513661715457026400 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This software was originally contributed by William A. Duff with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Strings; use Ada.Strings; with GNATCOLL.Paragraph_Filling.Words; use GNATCOLL.Paragraph_Filling.Words; with GNATCOLL.Paragraph_Filling.Badnesses; use GNATCOLL.Paragraph_Filling.Badnesses; package body GNATCOLL.Paragraph_Filling is use Word_Vectors; type Badness_Values is array (Word_Index range <>) of Badness_Value; type Word_Indexes is array (Word_Index range <>) of Word_Index; function Greedy_Fill (Paragraph : Paragraph_Filling.Words.Words; Max_Line_Length : Positive; Line_Prefix : String := "") return Unbounded_String; -- Formats a paragraph with the greedy algorithm (by putting as many words -- as possible on each line). function Nth_Index (Source : Unbounded_String; Pattern : String; N : Natural) return Natural; -- Returns the Nth instance of Pattern within Source. If the Nth index does -- not exist returns 0. function Can_And_Should_Move_A_Word (Max_Line_Length : Positive; Length_Of_First_Line : Positive; Length_Of_Second_Line : Positive; Length_Of_Last_Word : Positive) return Boolean; -- Decides whether the last word of the first line can fit on the second -- line and if so whether the badness is reduced by moving the word. function Count_Lines (Source : Unbounded_String) return Natural; -- Number of lines in Source -------------------------------- -- Can_And_Should_Move_A_Word -- -------------------------------- function Can_And_Should_Move_A_Word (Max_Line_Length : Positive; Length_Of_First_Line : Positive; Length_Of_Second_Line : Positive; Length_Of_Last_Word : Positive) return Boolean is begin if Length_Of_Second_Line + Length_Of_Last_Word <= Max_Line_Length and then not (Line_Badness (Length_Of_First_Line, Max_Line_Length) + Line_Badness (Length_Of_Second_Line, Max_Line_Length) < Line_Badness (Length_Of_First_Line - Length_Of_Last_Word, Max_Line_Length) + Line_Badness (Length_Of_Second_Line + Length_Of_Last_Word, Max_Line_Length)) then return True; else return False; end if; end Can_And_Should_Move_A_Word; ----------------- -- Count_Lines -- ----------------- function Count_Lines (Source : Unbounded_String) return Natural is Result : Natural := 0; begin for J in 1 .. Length (Source) loop if Element (Source, J) = ASCII.LF then Result := Result + 1; end if; end loop; return Result; end Count_Lines; ----------------- -- Greedy_Fill -- ----------------- function Greedy_Fill (Paragraph : Paragraph_Filling.Words.Words; Max_Line_Length : Positive; Line_Prefix : String := "") return Unbounded_String is Max : constant Integer := Max_Line_Length - Line_Prefix'Length; Result : Unbounded_String; Current : Natural; -- current line length begin if Paragraph.After_Last_Word /= 1 then Result := To_Unbounded_String (Line_Prefix) & Nth_Word (Paragraph, 1); Current := Length (Result); -- Go through the rest of the words, and for each one check if it -- fits on the current line. for Count in 2 .. Paragraph.After_Last_Word - 1 loop declare W : constant String := Nth_Word (Paragraph, Count); begin if Current + W'Length + 1 <= Max then Current := Current + 1; Append (Result, ' '); else Append (Result, ASCII.LF); Append (Result, Line_Prefix); Current := Line_Prefix'Length; end if; Current := Current + W'Length; Append (Result, W); end; end loop; Append (Result, ASCII.LF); end if; return Result; end Greedy_Fill; ----------------- -- Greedy_Fill -- ----------------- function Greedy_Fill (Paragraph : String; Max_Line_Length : Positive := Default_Max_Line_Length; Line_Prefix : String := "") return Ada.Strings.Unbounded.Unbounded_String is Para : String renames Paragraph; W : GNATCOLL.Paragraph_Filling.Words.Words := Index_Paragraph (Para); begin return Greedy_Fill (W, Max_Line_Length, Line_Prefix); end Greedy_Fill; ---------------- -- Knuth_Fill -- ---------------- function Knuth_Fill (Paragraph : String; Max_Line_Length : Positive := Default_Max_Line_Length; Line_Prefix : String := "") return Unbounded_String is Para : Paragraph_Filling.Words.Words := Index_Paragraph (Paragraph); function Calculate_Best (Paragraph : Paragraph_Filling.Words.Words; Max_Line_Length : Positive) return Word_Indexes; -- Determines the best division of lines and returns the word that -- begins each line in this setup as an array. function Determine_First_Words (Last_Word : Word_Count; Best_Words : Word_Indexes) return Word_Vector; -- Takes an array of the first words, reverses it, and stores it in a -- vector. -------------------- -- Calculate_Best -- -------------------- function Calculate_Best (Paragraph : Paragraph_Filling.Words.Words; Max_Line_Length : Positive) return Word_Indexes is Minimum_Badnesses : Badness_Values (1 .. Last_Word (Paragraph) + 1); Result : Word_Indexes (1 .. Last_Word (Paragraph)); begin Minimum_Badnesses (1) := Zero; for Y in 1 .. Last_Word (Paragraph) loop Minimum_Badnesses (Y + 1) := Infinity; for X in reverse 1 .. Y loop declare Badness : constant Badness_Value := Line_Badness (Paragraph, X, Y, Max_Line_Length) + Minimum_Badnesses (X); begin exit when Badness = Infinity; -- ??? Badness might be uninitialized here if Badness < Minimum_Badnesses (Y + 1) then Minimum_Badnesses (Y + 1) := Badness; Result (Y) := X; end if; end; end loop; end loop; for Count in Result'First .. Result'Last - 1 loop pragma Assert (Result (Count) <= Result (Count + 1)); null; end loop; return Result; end Calculate_Best; --------------------------- -- Determine_First_Words -- --------------------------- function Determine_First_Words (Last_Word : Word_Count; Best_Words : Word_Indexes) return Word_Vector is X : Word_Index; Y : Word_Index := Last_Word; Result : Word_Vector; begin Append (Result, Word_Index'Last); loop X := Best_Words (Y); Append (Result, X); exit when X = 1; pragma Assert (Y /= X - 1); Y := X - 1; end loop; return Result; end Determine_First_Words; -- Start of processing for Knuth_Fill begin if Paragraph = "" then return To_Unbounded_String (Paragraph); end if; return Merge_Lines (W => Para, Split_Before_Word => Determine_First_Words (Last_Word (Para), Calculate_Best (Para, Max_Line_Length)), Line_Prefix => Line_Prefix); end Knuth_Fill; -------------------- -- No_Fill -- -------------------- function No_Fill (Paragraph : String; Max_Line_Length : Positive := Default_Max_Line_Length; Line_Prefix : String := "") return Unbounded_String is pragma Unreferenced (Max_Line_Length, Line_Prefix); begin return To_Unbounded_String (Paragraph); end No_Fill; --------------- -- Nth_Index -- --------------- function Nth_Index (Source : Unbounded_String; Pattern : String; N : Natural) return Natural is Result : Natural := 0; begin for Count in 1 .. N loop Result := Index (Source, Pattern, Result + 1); -- If there are > N instances the next intstance after the last -- existing instance will return 0. If the loop continues to -- run Index will start from the beginning and will end on an -- arbitrary instance. exit when Result = 0; end loop; return Result; end Nth_Index; ----------------- -- Pretty_Fill -- ----------------- function Pretty_Fill (Paragraph : String; Max_Line_Length : Positive := Default_Max_Line_Length) return Unbounded_String is -- Since we will be changing the lines afterward, this is not compatible -- with the use of Line_Prefix. Result : Unbounded_String := Greedy_Fill (Paragraph, Max_Line_Length, Line_Prefix => ""); Number_Of_Lines : constant Natural := Count_Lines (Result); Did_Something : Boolean; Count : Natural := 0; begin -- Compares adjacent lines, starting with the first and second line -- and ending with the third to last and second to last lines. If -- Badness after moving the last word of the upper line to the next -- line is less than or equal to Badness of leaving the paragraph alone, -- then the word is moved. This is repeated through the whole paragraph -- (Max_Line_Length / 2) * Number_Of_Lines times or until nothing is -- changed in a loop of the paragraph. loop Count := Count + 1; Did_Something := False; for Line_Number in 1 .. Number_Of_Lines - 2 loop declare -- ??? Very inefficient, we keep searching the same pattern -- over and over. Index_0 : constant Natural := Nth_Index (Result, "" & ASCII.LF, Line_Number - 1); Index_1 : constant Natural := Nth_Index (Result, "" & ASCII.LF, Line_Number); Index_2 : constant Natural := Nth_Index (Result, "" & ASCII.LF, Line_Number + 1); Length_1 : constant Positive := Index_1 - Index_0 - 1; Length_2 : constant Positive := Index_2 - Index_1 - 1; Word_Start : constant Natural := Index (Result, " ", Index_1, Backward); Word_Length : constant Positive := Index_1 - Word_Start; begin if Can_And_Should_Move_A_Word (Max_Line_Length, Length_1, Length_2, Word_Length) then Replace_Element (Result, Word_Start, ASCII.LF); Replace_Element (Result, Index_1, ' '); Did_Something := True; end if; end; end loop; exit when not Did_Something; end loop; if Number_Of_Lines >= 4 then declare Index_0 : constant Natural := Nth_Index (Result, "" & ASCII.LF, Number_Of_Lines - 3); Index_1 : constant Natural := Nth_Index (Result, "" & ASCII.LF, Number_Of_Lines - 2); Index_2 : constant Natural := Nth_Index (Result, "" & ASCII.LF, Number_Of_Lines - 1); Index_3 : constant Natural := Nth_Index (Result, "" & ASCII.LF, Number_Of_Lines); Length_1 : constant Positive := Index_1 - Index_0 - 1; Length_2 : constant Positive := Index_2 - Index_1 - 1; Length_3 : constant Positive := Index_3 - Index_2 - 1; Word_Start : constant Natural := Index (Result, " ", Index_2, Backward); Word_Length : constant Positive := Index_2 - Word_Start; begin if Length_3 + Word_Length <= Max_Line_Length and then abs (Length_2 - Length_1) > abs (Length_2 - Length_1 - Word_Length) then Replace_Element (Result, Word_Start, ASCII.LF); Replace_Element (Result, Index_2, ' '); Did_Something := True; end if; end; elsif Number_Of_Lines = 3 then declare Index_0 : constant Natural := 0; Index_1 : constant Natural := Nth_Index (Result, "" & ASCII.LF, Number_Of_Lines - 2); Index_2 : constant Natural := Nth_Index (Result, "" & ASCII.LF, Number_Of_Lines - 1); Index_3 : constant Natural := Nth_Index (Result, "" & ASCII.LF, Number_Of_Lines); Length_1 : constant Positive := Index_1 - Index_0 - 1; Length_2 : constant Positive := Index_2 - Index_1 - 1; Length_3 : constant Positive := Index_3 - Index_2 - 1; Word_Start : constant Natural := Index (Result, " ", Index_2, Backward); Word_Length : constant Positive := Index_2 - Word_Start; begin if Length_3 + Word_Length <= Max_Line_Length and then abs (Length_2 - Length_1) > abs (Length_2 - Length_1 - Word_Length) then Replace_Element (Result, Word_Start, ASCII.LF); Replace_Element (Result, Index_2, ' '); Did_Something := True; end if; end; end if; return Result; end Pretty_Fill; end GNATCOLL.Paragraph_Filling; gnatcoll-core-21.0.0/src/paragraph_filling/gnatcoll-paragraph_filling-badnesses.ads0000644000175000017500000001017413661715457030357 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This software was originally contributed by William A. Duff with GNATCOLL.Paragraph_Filling.Words; use GNATCOLL.Paragraph_Filling.Words; private package GNATCOLL.Paragraph_Filling.Badnesses is type Badness_Value is private; Zero : constant Badness_Value; Infinity : constant Badness_Value; -- It's a non-negative number that represents how bad the formatting of a -- line or paragraph is, with Zero meaning perfectly good, and Infinity -- representing "too long to fit on a line". The term 'badness' comes from -- Knuth's TeX. function "+" (X, Y : Badness_Value) return Badness_Value; -- Adds two Badnesses. If the result is greater than infinity, it returns -- infinity. function "<" (X, Y : Badness_Value) return Boolean; -- Returns true if X is less than Y. Else returns False function "**" (X : Natural; Y : Positive) return Badness_Value; -- Raise X to the Y power and returns the result as a Badness_Value. If the -- result is greater that infinity, returns infinity. function Image (Badness : Badness_Value) return String; function Line_Badness (W : Paragraph_Filling.Words.Words; X, Y : Word_Index; Max_Line_Length : Positive; Format_Last_Line : Boolean := False) return Badness_Value; -- Returns badness as determined by a formula invented by Knuth. This -- formula calculates the square of the difference between the Line_Length -- (calculated by the Line_Length function in Paragraph_Filling.Words) and -- the Max_Line_Length. If the line length is greater than the -- Max_Line_Length then the function returns Infinity. -- -- ??? Currently, Format_Last_Line is always defaulted to false. However, -- the calls could be changes to allow a user option of whether to include -- the last line in badness calculations. function Line_Badness (Line_Length : Positive; Max_Line_Length : Positive) return Badness_Value; -- Returns badness as determined by a formula invented by Knuth. -- This formula calculates the square of the difference between the -- Line_Length and the Max_Line_Length. If the line length is greater -- than the Max_Line_Length then the function returns Infinity. private type Badness_Value is new Natural; Zero : constant Badness_Value := 0; Infinity : constant Badness_Value := Badness_Value'Last; end GNATCOLL.Paragraph_Filling.Badnesses; gnatcoll-core-21.0.0/src/gnatcoll-scripts-shell.adb0000644000175000017500000017710213661715457022050 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2003-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Containers.Indefinite_Vectors; with Ada.Exceptions; use Ada.Exceptions; with Ada.IO_Exceptions; use Ada.IO_Exceptions; with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Unchecked_Deallocation; with System.Address_Image; with System; use System; with GNAT.Debug_Utilities; use GNAT.Debug_Utilities; with GNATCOLL.Mmap; use GNATCOLL.Mmap; with GNAT.OS_Lib; use GNAT.OS_Lib; with GNATCOLL.Scripts; use GNATCOLL.Scripts; with GNATCOLL.Scripts.Impl; use GNATCOLL.Scripts.Impl; with GNATCOLL.Traces; use GNATCOLL.Traces; with GNATCOLL.Utils; use GNATCOLL.Utils; package body GNATCOLL.Scripts.Shell is Me : constant Trace_Handle := Create ("SHELL_SCRIPT", Off); Me_Log : constant Trace_Handle := Create ("SCRIPTS.LOG", Off); Cst_Prefix : constant String := "@cst@"; -- Prefix used to store the name of constants in class instances use Instances_List, Command_Hash; procedure Free_Internal_Data (Script : access Shell_Scripting_Record'Class); -- Free the internal memory used to store the results of previous commands -- and class instances. ---------- -- Misc -- ---------- function Name_From_Instance (Instance : access Class_Instance_Record'Class) return String; -- Return the string to display to report the instance in the shell function Instance_From_Name (Script : access Shell_Scripting_Record'Class; Name : String) return Class_Instance; -- Opposite of Name_From_Instance function Instance_From_Address (Script : access Shell_Scripting_Record'Class; Add : System.Address) return Class_Instance; -- Return an instance from its address function Execute_GPS_Shell_Command (Script : access Shell_Scripting_Record'Class; Command : String; Errors : access Boolean) return String; -- Execute a command in the GPS shell and returns its result. -- Command might be a series of commands, separated by semicolons or -- newlines. The return value is the result of the last command. -- If Errors is set to True on exit, then the return value is an error msg function Execute_GPS_Shell_Command (Script : access Shell_Scripting_Record'Class; CL : Arg_List; Errors : access Boolean) return String; -- Execute a command in the GPS shell and returns its result. -- Command must be a single command (no semicolon-separated list). procedure Module_Command_Handler (Data : in out Callback_Data'Class; Command : String); -- Handles functions specific to the shell language ------------------------ -- Internals Nth_Arg -- ------------------------ function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Success : access Boolean) return String; function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Success : access Boolean) return Unbounded_String; function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Success : access Boolean) return Subprogram_Type; function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Class : Class_Type; Allow_Null : Boolean; Success : access Boolean) return Class_Instance; -- These functions are called by the overridden Nth_Arg functions. They try -- to return the parameter at the location N. If no parameter is found, -- Success is false, true otherwise. It's the responsibility of the -- enclosing Nth_Arg to either raise a No_Such_Parameter exception or to -- return a default value. -------------------- -- Block_Commands -- -------------------- procedure Block_Commands (Script : access Shell_Scripting_Record; Block : Boolean) is begin Script.Blocked := Block; end Block_Commands; ------------------------ -- Name_From_Instance -- ------------------------ function Name_From_Instance (Instance : access Class_Instance_Record'Class) return String is begin return '<' & Get_Name (Shell_Class_Instance (Instance).Class) & "_0x" & System.Address_Image (Instance.all'Address) & '>'; end Name_From_Instance; ------------------------ -- Instance_From_Name -- ------------------------ function Instance_From_Name (Script : access Shell_Scripting_Record'Class; Name : String) return Class_Instance is Index : Natural := Name'First; begin if Name = "null" then return No_Class_Instance; end if; while Index <= Name'Last - 3 and then Name (Index .. Index + 2) /= "_0x" loop Index := Index + 1; end loop; return Instance_From_Address (Script, Value ("16#" & Name (Index + 3 .. Name'Last - 1) & "#")); exception when others => -- Invalid instance return No_Class_Instance; end Instance_From_Name; --------------------------- -- Instance_From_Address -- --------------------------- function Instance_From_Address (Script : access Shell_Scripting_Record'Class; Add : System.Address) return Class_Instance is L : Instances_List.Cursor := First (Script.Instances); begin while Has_Element (L) loop if Get_CIR (Element (L)).all'Address = Add then return Element (L); end if; Next (L); end loop; return No_Class_Instance; end Instance_From_Address; ----------------- -- Is_Subclass -- ----------------- function Is_Subclass (Instance : access Shell_Class_Instance_Record; Base : String) return Boolean is pragma Unreferenced (Instance, Base); begin -- ??? Not checked return True; end Is_Subclass; ------------------ -- Set_Property -- ------------------ overriding procedure Set_Property (Instance : access Shell_Class_Instance_Record; Name : String; Value : Integer) is begin -- We can only retrieve string constants later on, so convert here Set_Data (Instance, Cst_Prefix & Name, Create_Property (Image (Value, 0))); end Set_Property; overriding procedure Set_Property (Instance : access Shell_Class_Instance_Record; Name : String; Value : Float) is begin -- We can only retrieve string constants later on, so convert here Set_Data (Instance, Cst_Prefix & Name, Create_Property (Float'Image (Value))); end Set_Property; overriding procedure Set_Property (Instance : access Shell_Class_Instance_Record; Name : String; Value : Boolean) is begin Set_Data (Instance, Cst_Prefix & Name, Create_Property (Boolean'Image (Value))); end Set_Property; overriding procedure Set_Property (Instance : access Shell_Class_Instance_Record; Name : String; Value : String) is begin Set_Data (Instance, Cst_Prefix & Name, Create_Property (Value)); end Set_Property; --------------------- -- Name_Parameters -- --------------------- procedure Name_Parameters (Data : in out Shell_Callback_Data; Names : Cst_Argument_List) is pragma Unreferenced (Data, Names); begin null; end Name_Parameters; ---------------------------- -- Module_Command_Handler -- ---------------------------- procedure Module_Command_Handler (Data : in out Callback_Data'Class; Command : String) is begin if Command = "load" then declare Filename : constant String := Nth_Arg (Data, 1); File : Mapped_File; Errors : aliased Boolean; begin File := Open_Read (Filename); Read (File); declare Ignored : constant String := Execute_GPS_Shell_Command (Shell_Scripting (Get_Script (Data)), String (GNATCOLL.Mmap.Data (File)(1 .. Last (File))), Errors'Access); pragma Unreferenced (Ignored); begin null; end; exception when Name_Error => Set_Error_Msg (Data, "File not found: """ & Filename & '"'); end; elsif Command = "echo" or else Command = "echo_error" then declare Result : Unbounded_String; begin for A in 1 .. Number_Of_Arguments (Data) loop Append (Result, String'(Nth_Arg (Data, A))); if A /= Number_Of_Arguments (Data) then Append (Result, ' '); end if; end loop; if Command = "echo" then Insert_Text (Get_Script (Data), Txt => To_String (Result) & ASCII.LF); else Insert_Error (Get_Script (Data), Txt => To_String (Result) & ASCII.LF); end if; end; elsif Command = "clear_cache" then Free_Internal_Data (Shell_Scripting (Get_Script (Data))); end if; end Module_Command_Handler; ---------------- -- Initialize -- ---------------- procedure Initialize (Data : in out Shell_Callback_Data'Class; Script : access Shell_Scripting_Record'Class) is begin Data.Script := Shell_Scripting (Script); Data.Return_Value := null; Data.Return_Dict := null; Data.Return_As_List := False; Data.Return_As_Error := False; end Initialize; ------------------------------ -- Register_Shell_Scripting -- ------------------------------ procedure Register_Shell_Scripting (Repo : Scripts_Repository; Script : Shell_Scripting := null) is S : Shell_Scripting; begin if Script /= null then S := Script; else S := new Shell_Scripting_Record; end if; S.Repo := Repo; Register_Scripting_Language (Repo, S); Register_Command (Repo, "load", Minimum_Args => 1, Maximum_Args => 1, Handler => Module_Command_Handler'Access, Language => Shell_Name); Register_Command (Repo, "echo", Minimum_Args => 0, Maximum_Args => Natural'Last, Handler => Module_Command_Handler'Access, Language => Shell_Name); Register_Command (Repo, "echo_error", Minimum_Args => 0, Maximum_Args => Natural'Last, Handler => Module_Command_Handler'Access, Language => Shell_Name); Register_Command (Repo, "clear_cache", Handler => Module_Command_Handler'Access, Language => Shell_Name); end Register_Shell_Scripting; ------------------- -- List_Commands -- ------------------- procedure List_Commands (Script : access Shell_Scripting_Record'Class; Console : Virtual_Console := null) is package Command_List is new Ada.Containers.Indefinite_Vectors (Positive, String); package Ascending is new Command_List.Generic_Sorting ("<"); V : Command_List.Vector; begin -- Put all commands into V declare C : Command_Hash.Cursor := Script.Commands_List.First; begin while Has_Element (C) loop V.Append (Element (C).Command.all); Next (C); end loop; end; -- Sort commands Ascending.Sort (V); -- Output them declare C : Command_List.Cursor := V.First; begin while Command_List.Has_Element (C) loop Insert_Text (Script, Console, Command_List.Element (C) & ASCII.LF); Command_List.Next (C); end loop; end; end List_Commands; ----------------------- -- Register_Property -- ----------------------- overriding procedure Register_Property (Script : access Shell_Scripting_Record; Prop : Property_Descr_Access) is pragma Unreferenced (Script, Prop); begin -- All the work is done in Execute_Command null; end Register_Property; ---------------------- -- Register_Command -- ---------------------- overriding procedure Register_Command (Script : access Shell_Scripting_Record; Command : Command_Descr_Access) is Cmd : GNAT.Strings.String_Access; Info_C : Command_Hash.Cursor; Info : Command_Information_Access; begin if Command.Command = "" then return; end if; if Command.Class /= No_Class then if Command.Command = Constructor_Method then Cmd := new String'(Get_Name (Command.Class)); elsif Command.Command = Destructor_Method then Cmd := new String'(Get_Name (Command.Class) & ".__delete"); else Cmd := new String' (Get_Name (Command.Class) & "." & Command.Command); -- First parameter is always the instance end if; else Cmd := new String'(Command.Command); end if; Info_C := Find (Script.Commands_List, Cmd.all); -- Check that the command is not already registered if Has_Element (Info_C) then raise Program_Error with "Command already registered " & Cmd.all; else Info := new Command_Information' (Command => Cmd, Cmd => Command); Include (Script.Commands_List, Cmd.all, Info); end if; end Register_Command; -------------------- -- Register_Class -- -------------------- overriding procedure Register_Class (Script : access Shell_Scripting_Record; Name : String; Base : Class_Type := No_Class; Module : Module_Type := Default_Module) is pragma Unreferenced (Script, Name, Base, Module); begin -- Classes not supported in the shell module null; end Register_Class; -------------------- -- Display_Prompt -- -------------------- overriding procedure Display_Prompt (Script : access Shell_Scripting_Record; Console : Virtual_Console := null) is begin Insert_Prompt (Script, Console, Script.Prompt.all); end Display_Prompt; ---------------- -- Get_Prompt -- ---------------- overriding function Get_Prompt (Script : access Shell_Scripting_Record) return String is begin return Script.Prompt.all; end Get_Prompt; -------------- -- Complete -- -------------- procedure Complete (Script : access Shell_Scripting_Record; Input : String; Completions : out String_Lists.List) is Current : Command_Hash.Cursor; Info : Command_Information_Access; begin Completions := String_Lists.Empty_List; Current := First (Script.Commands_List); while Has_Element (Current) loop Info := Element (Current); declare S : constant String := Info.Command.all; begin if S'Length >= Input'Length and then S (S'First .. S'First + Input'Length - 1) = Input then String_Lists.Append (Completions, S); end if; end; Next (Current); end loop; String_Lists_Sort.Sort (Completions); end Complete; --------------------- -- Execute_Command -- --------------------- procedure Execute_Command (Script : access Shell_Scripting_Record; CL : Arg_List; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : out Boolean) is pragma Unreferenced (Show_Command); Old_Console : constant Virtual_Console := Script.Console; Err : aliased Boolean; begin if Console /= null then Script.Console := Console; end if; declare S : constant String := Execute_GPS_Shell_Command (Script, CL, Err'Unchecked_Access); begin Errors := Err; if S /= "" then Insert_Text (Script, Console, S & ASCII.LF); end if; Script.Console := Old_Console; -- Do not display the prompt in the shell console if we did not -- output to it if not Hide_Output and then (Console = null or else Console = Old_Console) then Display_Prompt (Script, Script.Console); end if; end; end Execute_Command; ------------------------------- -- Execute_Command_With_Args -- ------------------------------- function Execute_Command_With_Args (Script : access Shell_Scripting_Record; CL : Arg_List) return String is Errors : aliased Boolean; begin return Execute_GPS_Shell_Command (Script, CL, Errors'Unchecked_Access); end Execute_Command_With_Args; ------------------ -- Execute_File -- ------------------ procedure Execute_File (Script : access Shell_Scripting_Record; Filename : String; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : out Boolean) is Old_Console : constant Virtual_Console := Script.Console; Err : aliased Boolean; CL : Arg_List; begin if Console /= null then Script.Console := Console; end if; CL := Create ("load"); Append_Argument (CL, Filename, One_Arg); if Show_Command then Insert_Text (Script, Console, To_Display_String (CL)); end if; declare S : constant String := Execute_GPS_Shell_Command (Script, CL, Err'Unchecked_Access); begin Errors := Err; if S /= "" and then not Hide_Output then Insert_Text (Script, Console, S & ASCII.LF); end if; Script.Console := Old_Console; if not Hide_Output then Display_Prompt (Script, Script.Console); end if; end; end Execute_File; -------------- -- Get_Name -- -------------- function Get_Name (Script : access Shell_Scripting_Record) return String is pragma Unreferenced (Script); begin return Shell_Name; end Get_Name; ---------- -- Free -- ---------- procedure Free (Com : in out Command_Information_Access) is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Command_Information, Command_Information_Access); begin Free (Com.Command); Unchecked_Free (Com); end Free; ------------------------ -- Free_Internal_Data -- ------------------------ procedure Free_Internal_Data (Script : access Shell_Scripting_Record'Class) is begin for R in Script.Returns'Range loop Free (Script.Returns (R)); end loop; Script.Instances.Clear; end Free_Internal_Data; ------------- -- Destroy -- ------------- procedure Destroy (Script : access Shell_Scripting_Record) is C : Command_Hash.Cursor; Com : Command_Information_Access; begin Free_Internal_Data (Script); Free (Script.Prompt); Free (Script.Returns); C := First (Script.Commands_List); while Has_Element (C) loop Com := Element (C); Free (Com); Next (C); end loop; Script.Finalized := True; end Destroy; ---------------- -- Set_Prompt -- ---------------- procedure Set_Prompt (Script : access Shell_Scripting_Record'Class; Prompt : String) is begin Free (Script.Prompt); Script.Prompt := new String'(Prompt); end Set_Prompt; --------------------- -- Execute_Command -- --------------------- function Execute_Command (Script : access Shell_Scripting_Record; CL : Arg_List; Console : Virtual_Console := null; Hide_Output : Boolean := False; Show_Command : Boolean := True; Errors : access Boolean) return String is pragma Unreferenced (Show_Command); Err : aliased Boolean; Old_Console : constant Virtual_Console := Script.Console; begin if Console /= null then Script.Console := Console; end if; declare Result : constant String := Execute_GPS_Shell_Command (Script, CL, Err'Unchecked_Access); begin Errors.all := Err; if Result /= "" and then not Hide_Output then Insert_Text (Script, Console, Result & ASCII.LF); end if; Script.Console := Old_Console; if not Hide_Output then Display_Prompt (Script, Script.Console); end if; return Result; end; end Execute_Command; --------------------- -- Execute_Command -- --------------------- function Execute_Command (Script : access Shell_Scripting_Record; CL : Arg_List; Console : Virtual_Console := null; Hide_Output : Boolean := False; Errors : access Boolean) return Boolean is Old_Console : constant Virtual_Console := Script.Console; Err : aliased Boolean; begin if Console /= null then Script.Console := Console; end if; declare Result : constant String := Trim (Execute_GPS_Shell_Command (Script, CL, Err'Unchecked_Access), Ada.Strings.Both); begin Errors.all := Err; if not Hide_Output then Insert_Text (Script, Console, Result & ASCII.LF); end if; Script.Console := Old_Console; if not Hide_Output then Display_Prompt (Script, Script.Console); end if; return Result = "1" or else To_Lower (Result) = "true"; end; end Execute_Command; ------------------------------- -- Execute_GPS_Shell_Command -- ------------------------------- function Execute_GPS_Shell_Command (Script : access Shell_Scripting_Record'Class; CL : Arg_List; Errors : access Boolean) return String is Data_C : Command_Hash.Cursor; Data : Command_Information_Access; Instance : Class_Instance; Min, Max : Natural; Found : Boolean; Count : Natural; Command : constant String := Get_Command (CL); begin Errors.all := False; if Script.Finalized then return ""; end if; if Command = "" then return ""; end if; if Active (Me) then Trace (Me, "Executing " & To_Display_String (CL) & " blocked=" & Script.Blocked'Img); end if; if Script.Blocked then Errors.all := True; return "A command is already executing"; end if; if Active (Me_Log) then Trace (Me_Log, "Executing " & To_Display_String (CL, Max_Arg_Length => 100)); end if; -- Special case: access to instance constants if Command (Command'First) = '@' then Min := 1; Max := 2; Found := True; else Data_C := Find (Script.Commands_List, Command); Found := Has_Element (Data_C); if Found then Data := Element (Data_C); Min := Data.Cmd.Minimum_Args; Max := Data.Cmd.Maximum_Args; if Data.Cmd.Class /= No_Class and then not Data.Cmd.Static_Method and then Data.Cmd.Command /= Constructor_Method and then Data.Cmd.Command /= Destructor_Method then Min := Min + 1; if Max /= Natural'Last then Max := Max + 1; end if; end if; end if; end if; if Found then if Min <= Args_Length (CL) and then Args_Length (CL) <= Max then Count := Args_Length (CL); if Data /= null and then Data.Cmd.Command = Constructor_Method then Count := Count + 1; end if; declare Callback : Shell_Callback_Data'Class := Shell_Callback_Data'Class (Create (Script, Count)); -- The call above allocates Callback.Args, no need to do that -- below begin Callback.Script := Shell_Scripting (Script); Callback.CL := Create (""); if Data /= null and then Data.Cmd.Command = Constructor_Method then Instance := New_Instance (Callback.Script, Data.Cmd.Class); Append_Argument (Callback.CL, Name_From_Instance (Get_CIR (Instance)), One_Arg); end if; for A in 1 .. Args_Length (CL) loop declare Args_A : constant String := Nth_Arg (CL, A); begin if Args_A'Length > 0 and then Args_A (Args_A'First) = '%' then declare Num : Integer; begin Num := Integer'Value (Args_A (Args_A'First + 1 .. Args_A'Last)); Append_Argument (Callback.CL, Script.Returns (Num + Script.Returns'First - 1).all, One_Arg); exception when Constraint_Error => Append_Argument (Callback.CL, Args_A, One_Arg); end; else Append_Argument (Callback.CL, Args_A, One_Arg); end if; end; end loop; if Data = null then -- Accessing a field Instance := Nth_Arg (Callback, 1, Any_Class); -- To match python, we first check for simple properties declare Prop : constant Instance_Property := Get_Data (Instance, Cst_Prefix & Command (Command'First + 1 .. Command'Last)); P : Property_Descr_Access; begin if Prop /= null then Trace (Me, "A simple property"); if Number_Of_Arguments (Callback) = 2 then Errors.all := True; return "Property is read-only: " & Command (Command'First + 1 .. Command'Last); end if; Set_Return_Value (Callback, As_String (Prop.all)); else Trace (Me, "A setter/getter property args=" & Number_Of_Arguments (Callback)'Img); -- Does this correspond to a setter/getter property ? P := Script.Repo.Properties; while P /= null loop exit when P.Class = Shell_Class_Instance (Get_CIR (Instance)).Class and then P.Name = Command (Command'First + 1 .. Command'Last); P := P.Next; end loop; if P = null then Errors.all := True; return "Command not recognized: " & Command; end if; if Number_Of_Arguments (Callback) = 1 then if P.Getter = null then Trace (Me, "Property is read-only"); Errors.all := True; return "Property is write-only: " & Command (Command'First + 1 .. Command'Last); end if; P.Getter (Callback, P.Name); -- Already set the return value, nothing else to do else if P.Setter = null then Errors.all := True; return "Property is read-only: " & Command (Command'First + 1 .. Command'Last); end if; P.Setter (Callback, P.Name); -- Already set the value, nothing else to do end if; end if; end; else Data.Cmd.Handler (Callback, Data.Cmd.Command); if Callback.Return_As_Error then Errors.all := True; Free (Callback.Return_Dict); declare R : constant String := Callback.Return_Value.all; begin Free (Callback.Return_Value); return R; end; end if; if Data.Cmd.Command = Constructor_Method then Set_Return_Value (Callback, Instance); end if; if Callback.Return_Dict /= null then Free (Callback.Return_Value); Callback.Return_Value := Callback.Return_Dict; Callback.Return_Dict := null; end if; end if; -- Save the return value for the future Free (Script.Returns (Script.Returns'Last)); Script.Returns (Script.Returns'First + 1 .. Script.Returns'Last) := Script.Returns (Script.Returns'First .. Script.Returns'Last - 1); if Callback.Return_Value = null then Script.Returns (Script.Returns'First) := new String'(""); else Script.Returns (Script.Returns'First) := Callback.Return_Value; end if; if Callback.Return_Value = null then return ""; else -- Do not free Callback.Return_Value, it is stored in the -- list of previous commands return Callback.Return_Value.all; end if; end; else Errors.all := True; return "Incorrect number of arguments for " & Command; end if; end if; Errors.all := True; return "Command not recognized: " & Command; exception when Invalid_Parameter => Errors.all := True; return "Invalid parameter for " & Command; when E : others => Errors.all := True; return Exception_Information (E); end Execute_GPS_Shell_Command; ------------------------------- -- Execute_GPS_Shell_Command -- ------------------------------- function Execute_GPS_Shell_Command (Script : access Shell_Scripting_Record'Class; Command : String; Errors : access Boolean) return String is CL : Arg_List; First, Last : Integer; Quoted : Boolean; Triple_Quoted : Boolean; begin Errors.all := False; if Command /= "" then First := Command'First; while First <= Command'Last loop while First <= Command'Last and then (Command (First) = ' ' or else Command (First) = ASCII.HT) loop First := First + 1; end loop; if First > Command'Last then exit; end if; Last := First; Quoted := False; Triple_Quoted := False; -- Search until the beginning of the next command (separated by -- semicolon or newline). while Last <= Command'Last loop exit when not Quoted and then not Triple_Quoted and then (Command (Last) = ';' or else Command (Last) = ASCII.LF); if Command (Last) = '"' then if Last <= Command'Last - 2 and then Command (Last + 1) = '"' and then Command (Last + 2) = '"' then Triple_Quoted := not Triple_Quoted; Last := Last + 2; elsif not Triple_Quoted then Quoted := not Quoted; end if; elsif Command (Last) = '\' and then Last < Command'Last then Last := Last + 1; end if; Last := Last + 1; end loop; if Last - 1 >= First then CL := Parse_String (Command (First .. Last - 1), Command_Line_Treatment (Script)); if CL = Empty_Command_Line then Errors.all := True; return "Couldn't parse argument string for " & Command (First .. Last - 1); else declare R : constant String := Execute_GPS_Shell_Command (Script, CL => CL, Errors => Errors); begin if Last > Command'Last then return R; end if; end; end if; end if; First := Last + 1; end loop; end if; return ""; end Execute_GPS_Shell_Command; ---------------- -- Get_Script -- ---------------- function Get_Script (Data : Shell_Callback_Data) return Scripting_Language is begin return Scripting_Language (Data.Script); end Get_Script; -------------------- -- Get_Repository -- -------------------- function Get_Repository (Script : access Shell_Scripting_Record) return Scripts_Repository is begin return Script.Repo; end Get_Repository; -------------------- -- Current_Script -- -------------------- function Current_Script (Script : access Shell_Scripting_Record) return String is pragma Unreferenced (Script); begin return ""; end Current_Script; ------------------------- -- Number_Of_Arguments -- ------------------------- function Number_Of_Arguments (Data : Shell_Callback_Data) return Natural is begin return Args_Length (Data.CL); end Number_Of_Arguments; ---------- -- Free -- ---------- procedure Free (Data : in out Shell_Callback_Data) is begin Free (Data.Return_Value); Free (Data.Return_Dict); end Free; ----------- -- Clone -- ----------- function Clone (Data : Shell_Callback_Data) return Callback_Data'Class is New_CL : Arg_List := Create (Get_Command (Data.CL)); begin for A in 1 .. Args_Length (Data.CL) loop Append_Argument (New_CL, Nth_Arg (Data.CL, A), One_Arg); end loop; return Shell_Callback_Data' (Callback_Data with CL => New_CL, Script => Data.Script, Return_Value => null, Return_Dict => null, Return_As_List => False, Return_As_Error => False); end Clone; ------------ -- Create -- ------------ function Create (Script : access Shell_Scripting_Record; Arguments_Count : Natural) return Callback_Data'Class is Data : constant Shell_Callback_Data := (Callback_Data with Script => Shell_Scripting (Script), CL => Empty_Command_Line, Return_Value => null, Return_Dict => null, Return_As_List => False, Return_As_Error => False); pragma Unreferenced (Arguments_Count); begin return Data; end Create; ----------------- -- Set_Nth_Arg -- ----------------- procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : Subprogram_Type) is begin Set_Nth_Arg (Data.CL, N, Shell_Subprogram_Record (Value.all).Command.all); end Set_Nth_Arg; ----------------- -- Set_Nth_Arg -- ----------------- procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : String) is begin Set_Nth_Arg (Data.CL, N, Value); end Set_Nth_Arg; ----------------- -- Set_Nth_Arg -- ----------------- procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : Integer) is begin Set_Nth_Arg (Data.CL, N, Integer'Image (Value)); end Set_Nth_Arg; ----------------- -- Set_Nth_Arg -- ----------------- procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : Float) is begin Set_Nth_Arg (Data.CL, N, Float'Image (Value)); end Set_Nth_Arg; ----------------- -- Set_Nth_Arg -- ----------------- procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : Boolean) is begin Set_Nth_Arg (Data.CL, N, Boolean'Image (Value)); end Set_Nth_Arg; ----------------- -- Set_Nth_Arg -- ----------------- procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : Class_Instance) is begin Set_Nth_Arg (Data.CL, N, Name_From_Instance (Get_CIR (Value))); end Set_Nth_Arg; ----------------- -- Set_Nth_Arg -- ----------------- overriding procedure Set_Nth_Arg (Data : in out Shell_Callback_Data; N : Positive; Value : List_Instance) is begin Set_Nth_Arg (Data.CL, N, '(' & Get_Command (Shell_Callback_Data (Value).CL) & ')'); end Set_Nth_Arg; -------------- -- New_List -- -------------- overriding function New_List (Script : access Shell_Scripting_Record; Class : Class_Type := No_Class) return List_Instance'Class is pragma Unreferenced (Class); List : Shell_Callback_Data; begin List.Script := Shell_Scripting (Script); List.CL := Empty_Command_Line; return List; end New_List; ------------- -- Nth_Arg -- ------------- overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return List_Instance'Class is List : Shell_Callback_Data; begin List.Script := Data.Script; if N > Args_Length (Data.CL) then List.CL := Empty_Command_Line; -- An empty list else List.CL := Parse_String (Nth_Arg (Data.CL, N), Separate_Args); end if; return List; end Nth_Arg; ------------- -- Nth_Arg -- ------------- overriding function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Dictionary_Instance'Class is begin raise Program_Error with "Dictionary is not supported by language"; return Nth_Arg (Data, N); end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Success : access Boolean) return String is begin if N > Args_Length (Data.CL) then Success.all := False; return ""; else Success.all := True; return Nth_Arg (Data.CL, N); end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Success : access Boolean) return Unbounded_String is begin if N > Args_Length (Data.CL) then Success.all := False; return Null_Unbounded_String; else Success.all := True; return Nth_Arg (Data.CL, N); end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Class : Class_Type; Allow_Null : Boolean; Success : access Boolean) return Class_Instance is Class_Name : constant String := Nth_Arg (Data, N, Success); Ins : Class_Instance; begin if not Success.all then return No_Class_Instance; end if; Ins := Instance_From_Name (Data.Script, Class_Name); if Ins = No_Class_Instance and then Allow_Null then return No_Class_Instance; end if; if Ins = No_Class_Instance or else (Class /= Any_Class and then not Is_Subclass (Ins, Get_Name (Class))) then raise Invalid_Parameter; else return Ins; end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Success : access Boolean) return Subprogram_Type is Name : constant String := Nth_Arg (Data, N, Success); begin if not Success.all then return null; else return new Shell_Subprogram_Record' (Subprogram_Record with Script => Get_Script (Data), Command => new String'(Name)); end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Boolean is Success : aliased Boolean; S : constant String := Nth_Arg (Data, N, Success'Access); begin if Success then return Boolean'Value (S); else raise No_Such_Parameter; end if; exception when Constraint_Error => raise Invalid_Parameter; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Integer is Success : aliased Boolean; S : constant String := Nth_Arg (Data, N, Success'Access); begin if Success then return Integer'Value (S); else raise No_Such_Parameter; end if; exception when Constraint_Error => raise Invalid_Parameter; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Float is Success : aliased Boolean; S : constant String := Nth_Arg (Data, N, Success'Access); begin if Success then return Float'Value (S); else raise No_Such_Parameter; end if; exception when Constraint_Error => raise Invalid_Parameter; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return String is Success : aliased Boolean; Result : constant String := Nth_Arg (Data, N, Success'Access); begin if not Success then raise No_Such_Parameter; else return Result; end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Unbounded_String is Success : aliased Boolean; Result : constant Unbounded_String := Nth_Arg (Data, N, Success'Access); begin if not Success then raise No_Such_Parameter; else return Result; end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive) return Subprogram_Type is Success : aliased Boolean; Result : constant Subprogram_Type := Nth_Arg (Data, N, Success'Access); begin if not Success then raise No_Such_Parameter; else return Result; end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Class : Class_Type; Allow_Null : Boolean := False) return Class_Instance is Success : aliased Boolean; Result : constant Class_Instance := Nth_Arg (Data, N, Class, Allow_Null, Success'Access); begin if not Success then raise No_Such_Parameter; else return Result; end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Default : String) return String is Success : aliased Boolean; Result : constant String := Nth_Arg (Data, N, Success'Access); begin if not Success then return Default; else return Result; end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Default : Integer) return Integer is Success : aliased Boolean; Result : constant String := Nth_Arg (Data, N, Success'Access); begin if not Success then return Default; else return Integer'Value (Result); end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Default : Float) return Float is Success : aliased Boolean; Result : constant String := Nth_Arg (Data, N, Success'Access); begin if not Success then return Default; else return Float'Value (Result); end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Default : Boolean) return Boolean is Success : aliased Boolean; Result : constant String := Nth_Arg (Data, N, Success'Access); begin if not Success then return Default; else return Boolean'Value (Result); end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Class : Class_Type := Any_Class; Default : Class_Instance; Allow_Null : Boolean := False) return Class_Instance is Success : aliased Boolean; Result : constant Class_Instance := Nth_Arg (Data, N, Class, Allow_Null, Success'Access); begin if not Success then return Default; else return Result; end if; end Nth_Arg; ------------- -- Nth_Arg -- ------------- function Nth_Arg (Data : Shell_Callback_Data; N : Positive; Default : Subprogram_Type) return Subprogram_Type is Success : aliased Boolean; Result : constant Subprogram_Type := Nth_Arg (Data, N, Success'Access); begin if not Success then return Default; else return Result; end if; end Nth_Arg; ------------------- -- Set_Error_Msg -- ------------------- procedure Set_Error_Msg (Data : in out Shell_Callback_Data; Msg : String) is begin Free (Data.Return_Value); Data.Return_As_Error := True; Data.Return_Value := new String'(Msg); end Set_Error_Msg; ------------------------------ -- Set_Return_Value_As_List -- ------------------------------ overriding procedure Set_Return_Value_As_List (Data : in out Shell_Callback_Data; Size : Natural := 0; Class : Class_Type := No_Class) is pragma Unreferenced (Size, Class); begin Data.Return_As_List := True; end Set_Return_Value_As_List; -------------------------- -- Set_Return_Value_Key -- -------------------------- procedure Set_Return_Value_Key (Data : in out Shell_Callback_Data; Key : String; Append : Boolean := False) is pragma Unreferenced (Append); Tmp : GNAT.Strings.String_Access; begin if Data.Return_Value = null then if Data.Return_Dict = null then Data.Return_Dict := new String'(Key & " => ()"); else Tmp := Data.Return_Dict; Data.Return_Dict := new String'(Tmp.all & ", " & Key & " => ()"); Free (Tmp); end if; else if Data.Return_Dict = null then Data.Return_Dict := new String' (Key & " => (" & Data.Return_Value.all & ')'); else Tmp := Data.Return_Dict; Data.Return_Dict := new String' (Tmp.all & ", " & Key & " => (" & Data.Return_Value.all & ')'); Free (Tmp); end if; end if; Data.Return_As_List := False; Free (Data.Return_Value); end Set_Return_Value_Key; -------------------------- -- Set_Return_Value_Key -- -------------------------- procedure Set_Return_Value_Key (Data : in out Shell_Callback_Data; Key : Integer; Append : Boolean := False) is begin Set_Return_Value_Key (Data, Integer'Image (Key), Append); end Set_Return_Value_Key; -------------------------- -- Set_Return_Value_Key -- -------------------------- procedure Set_Return_Value_Key (Data : in out Shell_Callback_Data; Key : Class_Instance; Append : Boolean := False) is begin Set_Return_Value_Key (Data, Name_From_Instance (Get_CIR (Key)), Append); end Set_Return_Value_Key; ---------------------- -- Set_Return_Value -- ---------------------- procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : Integer) is begin if not Data.Return_As_List then Free (Data.Return_Value); end if; Set_Return_Value (Data, Integer'Image (Value)); end Set_Return_Value; overriding procedure Set_Address_Return_Value (Data : in out Shell_Callback_Data; Value : System.Address) is begin if not Data.Return_As_List then Free (Data.Return_Value); end if; Set_Return_Value (Data, System.Address_Image (Value)); end Set_Address_Return_Value; ---------------------- -- Set_Return_Value -- ---------------------- procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : Float) is begin if not Data.Return_As_List then Free (Data.Return_Value); end if; Set_Return_Value (Data, Float'Image (Value)); end Set_Return_Value; ---------------------- -- Set_Return_Value -- ---------------------- procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : Boolean) is begin if not Data.Return_As_List then Free (Data.Return_Value); end if; Set_Return_Value (Data, Boolean'Image (Value)); end Set_Return_Value; ---------------------- -- Set_Return_Value -- ---------------------- procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : String) is Tmp : GNAT.Strings.String_Access; begin if Data.Return_As_List and then Data.Return_Value /= null then Tmp := Data.Return_Value; Data.Return_Value := new String (1 .. Tmp'Length + 1 + Value'Length); Data.Return_Value (1 .. Tmp'Length) := Tmp.all; Data.Return_Value (Tmp'Length + 1) := ASCII.LF; Data.Return_Value (Tmp'Length + 2 .. Data.Return_Value'Last) := Value; Free (Tmp); else Free (Data.Return_Value); Data.Return_Value := new String'(Value); end if; end Set_Return_Value; ---------------------- -- Set_Return_Value -- ---------------------- procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : Class_Instance) is begin if Value = No_Class_Instance then Set_Return_Value (Data, String'("null")); else Set_Return_Value (Data, Name_From_Instance (Get_CIR (Value))); end if; end Set_Return_Value; ---------------------- -- Set_Return_Value -- ---------------------- overriding procedure Set_Return_Value (Data : in out Shell_Callback_Data; Value : List_Instance) is begin Set_Return_Value (Data, '(' & Get_Command (Shell_Callback_Data (Value).CL) & ')'); end Set_Return_Value; ------------------ -- New_Instance -- ------------------ function New_Instance (Script : access Shell_Scripting_Record; Class : Class_Type) return Class_Instance is Instance : Shell_Class_Instance; begin Instance := new Shell_Class_Instance_Record; Instance.Class := Class; Instance.Script := Script; return R : Class_Instance do CI_Pointers.Set (R.Ref, Instance); Instances_List.Prepend (Script.Instances, R); end return; end New_Instance; ---------------- -- Get_Method -- ---------------- overriding function Get_Method (Instance : access Shell_Class_Instance_Record; Name : String) return Subprogram_Type is Inst_Name : constant String := Name_From_Instance (Instance); begin return new Shell_Subprogram_Record' (Script => Scripting_Language (Instance.Script), Command => new String' (Get_Name (Instance.Class) & "." & Name & " " & Inst_Name)); end Get_Method; -------------------- -- Print_Refcount -- -------------------- function Print_Refcount (Instance : access Shell_Class_Instance_Record) return String is pragma Unreferenced (Instance); begin return ""; end Print_Refcount; ------------------------ -- Execute_Expression -- ------------------------ overriding procedure Execute_Expression (Result : in out Shell_Callback_Data; Expression : String; Hide_Output : Boolean := True) is pragma Unreferenced (Hide_Output); Errors : aliased Boolean; begin Result.Set_Return_Value (String'(Execute_GPS_Shell_Command (Shell_Scripting (Get_Script (Result)), Expression, Errors'Unchecked_Access))); end Execute_Expression; --------------------- -- Execute_Command -- --------------------- function Execute_Command (Script : access Shell_Scripting_Record; Command : String; Args : Callback_Data'Class) return Boolean is Errors : aliased Boolean; CL : Arg_List := Create (Command); begin for J in 1 .. Args_Length (Shell_Callback_Data (Args).CL) loop Append_Argument (CL, Nth_Arg (Shell_Callback_Data (Args).CL, J), One_Arg); end loop; declare Result : constant String := Trim (Execute_GPS_Shell_Command (Script, CL, Errors'Unchecked_Access), Ada.Strings.Both); begin return Result = "1" or else To_Lower (Result) = "true"; end; end Execute_Command; ------------- -- Execute -- ------------- overriding function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return Boolean is begin Error.all := False; return To_Lower (Execute (Shell_Subprogram (Subprogram), Args)) = "true"; end Execute; ------------- -- Execute -- ------------- function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return String is D : constant Shell_Callback_Data := Shell_Callback_Data (Args); CL : Arg_List; begin CL := Create (Subprogram.Command.all); for Arg in 1 .. Args_Length (D.CL) loop Append_Argument (CL, Nth_Arg (D.CL, Arg), One_Arg); end loop; return Execute_GPS_Shell_Command (Script => Shell_Scripting (Subprogram.Script), CL => CL, Errors => Error); end Execute; ------------- -- Execute -- ------------- overriding function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return Class_Instance is Result : constant String := Execute (Subprogram, Args, Error); begin return Instance_From_Name (Shell_Scripting (Subprogram.Script), Result); end Execute; ------------- -- Execute -- ------------- overriding function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return List_Instance'Class is List : Shell_Callback_Data; begin List.Script := Shell_Scripting (Subprogram.Script); List.CL := Parse_String (Execute (Subprogram, Args, Error), Separate_Args); return List; end Execute; ------------- -- Execute -- ------------- function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return GNAT.Strings.String_List is pragma Unreferenced (Subprogram, Args); begin Error.all := True; -- ??? We are in asynchronous mode, see Execute for String above return (1 .. 0 => null); end Execute; ------------- -- Execute -- ------------- overriding function Execute (Subprogram : access Shell_Subprogram_Record; Args : Callback_Data'Class; Error : not null access Boolean) return Any_Type is pragma Unreferenced (Subprogram, Args); begin Error.all := True; -- Any_Type is not supported for shell scripts raise Program_Error; return Empty_Any_Type; end Execute; -------------- -- Get_Name -- -------------- function Get_Name (Subprogram : access Shell_Subprogram_Record) return String is begin return "command: " & Subprogram.Command.all; end Get_Name; ---------- -- Free -- ---------- procedure Free (Subprogram : in out Shell_Subprogram_Record) is begin Free (Subprogram.Command); end Free; ---------------- -- Get_Script -- ---------------- function Get_Script (Subprogram : Shell_Subprogram_Record) return Scripting_Language is begin return Subprogram.Script; end Get_Script; ----------------- -- Get_Command -- ----------------- function Get_Command (Subprogram : access Shell_Subprogram_Record) return String is begin return Subprogram.Command.all; end Get_Command; ---------------- -- Initialize -- ---------------- procedure Initialize (Subprogram : in out Shell_Subprogram_Record'Class; Script : access Scripting_Language_Record'Class; Command : String) is begin Free (Subprogram.Command); Subprogram.Command := new String'(Command); Subprogram.Script := Scripting_Language (Script); end Initialize; -------------- -- Get_Args -- -------------- function Get_Args (Data : Shell_Callback_Data) return GNAT.OS_Lib.Argument_List is begin return To_List (Data.CL, False); -- ??? There is a memory leak here. Maybe we can get rid of this -- subprogram? end Get_Args; ---------------------------- -- Command_Line_Treatment -- ---------------------------- overriding function Command_Line_Treatment (Script : access Shell_Scripting_Record) return Command_Line_Mode is pragma Unreferenced (Script); begin return Separate_Args; end Command_Line_Treatment; --------------------- -- Execute_Command -- --------------------- overriding procedure Execute_Command (Args : in out Shell_Callback_Data; Command : String; Hide_Output : Boolean := True) is pragma Unreferenced (Hide_Output); Script : constant Shell_Scripting := Shell_Scripting (Get_Script (Args)); Errors : aliased Boolean; CL : Arg_List := Create (Command); begin for J in 1 .. Args_Length (Args.CL) loop Append_Argument (CL, Nth_Arg (Args.CL, J), One_Arg); end loop; declare Result : constant String := Trim (Execute_GPS_Shell_Command (Script, CL, Errors'Unchecked_Access), Ada.Strings.Both); begin Free (Args.Return_Value); Args.Return_Value := new String'(Result); end; end Execute_Command; ------------------ -- Return_Value -- ------------------ overriding function Return_Value (Data : Shell_Callback_Data) return String is begin if Data.Return_Value = null then raise Invalid_Parameter with "No return value"; else return Data.Return_Value.all; end if; end Return_Value; ------------------ -- Return_Value -- ------------------ overriding function Return_Value (Data : Shell_Callback_Data) return Integer is begin return Integer'Value (Return_Value (Data)); end Return_Value; ------------------ -- Return_Value -- ------------------ overriding function Return_Value (Data : Shell_Callback_Data) return Float is begin return Float'Value (Return_Value (Data)); end Return_Value; ------------------ -- Return_Value -- ------------------ overriding function Return_Value (Data : Shell_Callback_Data) return Boolean is begin return Boolean'Value (Return_Value (Data)); end Return_Value; ------------------ -- Return_Value -- ------------------ overriding function Return_Value (Data : Shell_Callback_Data) return Class_Instance is begin return Instance_From_Name (Data.Script, Return_Value (Data)); end Return_Value; ------------------ -- Return_Value -- ------------------ overriding function Return_Value (Data : Shell_Callback_Data) return List_Instance'Class is List : Shell_Callback_Data; begin List.Script := Data.Script; List.CL := Parse_String (Return_Value (Data), Separate_Args); return List; end Return_Value; ------------------- -- Get_User_Data -- ------------------- overriding function Get_User_Data (Self : not null access Shell_Class_Instance_Record) return access User_Data_List is begin return Self.Props'Access; end Get_User_Data; end GNATCOLL.Scripts.Shell; gnatcoll-core-21.0.0/src/gnatcoll-email-parser.ads0000644000175000017500000001114013661715457021643 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2018, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides a parser that creates an email message from a textual -- representation of it. with GNATCOLL.VFS; package GNATCOLL.Email.Parser is procedure Parse (Str : String; Msg : out Message); -- Default message parser procedure Parse_Ignore_Headers (Str : String; Msg : out Message); -- Same as Parse, but the headers are not stored into the final Msg. -- This significantly speeds up the parser, and should be used if you don't -- need access to headers later on. procedure Parse_Minimal_Headers (Str : String; Msg : out Message); -- Same as Parse, but only keep a subset of the headers. This removes -- headers like 'Received:', which are generally not useful to manipulate -- the message. procedure Parse_No_Payload (Str : String; Msg : out Message); -- Parse the message, but store its body unparsed (i.e. nested parts are -- not analyzed). procedure Parse_No_Payload_Minimal_Headers (Str : String; Msg : out Message); -- Parse the message, but store its body unparsed (i.e. nested parts are -- not analyzed). Ignore headers that are generally not useful to -- manipulate a message. type Header_Filter is access function (Name : String) return Boolean; procedure Full_Parse (Str : String; Msg : out Message; Store_Headers : Boolean := True; Store_Payload : Boolean := True; Parse_Payload : Boolean := True; Filter : Header_Filter := null); -- Internal version of Parse. You could implement your own Parse by -- calling this one with the appropriate parameters. For instance, you -- can choose the list of headers to store. -- If Store_Headers is false, then the headers will not be stored in the -- final message. Some of them are still taken into account to properly -- parse the message (MIME contents,...). This significantly speeds up the -- processing since less memory needs to be allocated. -- If Filter is specified, only those headers matching Filter will be -- stored. If Store_Headers is False, no header is stored. -- If Store_Payload is False, then the payload is not analyzed nor parsed, -- simply ignored. When the payload is stored, it can additionally be -- parsed, i.e. when it is a multi-part message, each of the part is -- extracted separately. To save time, they are not MIME-decoded though. procedure Full_Parse_From_File (Filename : GNATCOLL.VFS.Virtual_File; Msg : out Message; Store_Headers : Boolean := True; Store_Payload : Boolean := True; Parse_Payload : Boolean := True; Filter : Header_Filter := null); -- Same as Full_Parse, but reads the message directly from a file. -- Name_Error is raised if the file could not be read. procedure Parse_Payload (Msg : in out Message); -- Parse previously unparsed payload end GNATCOLL.Email.Parser; gnatcoll-core-21.0.0/src/gnatcoll-pools.adb0000644000175000017500000002703213661715457020404 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2010-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with Ada.Unchecked_Deallocation; with GNATCOLL.Refcount; use GNATCOLL.Refcount; with GNATCOLL.Traces; use GNATCOLL.Traces; with Interfaces; use Interfaces; package body GNATCOLL.Pools is use Pointers; Me : constant Trace_Handle := Create ("Pools"); type Pool_Array is array (Positive range <>) of Pool_Resource_Access; type Pool_Array_Access is access all Pool_Array; type Resource_Set_Data is record Elements : Pool_Array_Access; Param : aliased Factory_Param; Available : aliased Integer_32 := 0; end record; type Sets is array (Resource_Set range <>) of Resource_Set_Data; type Sets_Access is access all Sets; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Pool_Resource, Pool_Resource_Access); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Pool_Array, Pool_Array_Access); procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Sets, Sets_Access); protected type Pool is entry Get (Resource_Set) (Element : out Resource'Class); -- Get one resource -- You must have called Set_Factory before. -- The resource must be released explicitly by calling Release, or -- there will be starvation procedure Release (In_Pool : in out Pool_Resource_Access; Set : Resource_Set); -- Release the resource, and make it available to others. -- In_Pool might have been freed on exit procedure Set_Factory (Descr : Factory_Param; Max_Elements : Positive; Set : Resource_Set); -- Describe how to connect to the database. This can be called only -- once ie before getting the first connection procedure Free; -- Detach all resources from the pool. -- If they are in use elsewhere they will not be freed immediately, only -- when they are no longer in use. function Get_Factory_Param (Set : Resource_Set) return access Factory_Param; private Elements : Sets_Access; end Pool; protected body Pool is ----------------- -- Set_Factory -- ----------------- procedure Set_Factory (Descr : Factory_Param; Max_Elements : Positive; Set : Resource_Set) is begin if Elements = null then Elements := new Sets (Resource_Set'Range); end if; if Elements (Set).Elements = null then Elements (Set) := (Elements => new Pool_Array'(1 .. Max_Elements => null), Available => Integer_32 (Max_Elements), Param => Descr); else raise Program_Error with "Set_Factory can be called only once per resource_set"; end if; end Set_Factory; ----------------------- -- Get_Factory_Param -- ----------------------- function Get_Factory_Param (Set : Resource_Set) return access Factory_Param is begin return Elements (Set).Param'Access; end Get_Factory_Param; --------- -- Get -- --------- entry Get (for Set in Resource_Set) (Element : out Resource'Class) when Elements (Set).Available > 0 is In_Pool : Resource_Data; begin Elements (Set).Available := Elements (Set).Available - 1; -- Get the first available resource. Since they are allocated -- sequentially, this ensures that we preferably reuse an existing -- connection rather than create a new one. for E in Elements (Set).Elements'Range loop if Elements (Set).Elements (E) = null then -- ??? Issue: the factory might take a long time (for -- instance establishing a database connection). During -- that time, all threads waiting on Get are blocked. -- We should mark the slot as no longer available, and -- initialize the resource once returned to the user. Trace (Me, "Get: creating resource, at index" & E'Img); -- We have to cheat with the refcounting temporarily: the -- above call, if initialized at refcount=1, would call -- adjust once, and then finalize, thus try to call Release, -- resulting in a deadlock. Instead, we start with an -- off-by-one refcount, and put things back straight afterward. Elements (Set).Elements (E) := new Pool_Resource' (Element => Factory (Elements (Set).Param), Available => False); In_Pool := Resource_Data' (Set => Set, In_Set => Elements (Set).Elements (E)); Element.Set (In_Pool); return; elsif Elements (Set).Elements (E).Available then if Active (Me) then Trace (Me, "Get: pool " & Set'Img & " returning resources at index" & E'Img); end if; Elements (Set).Elements (E).Available := False; In_Pool := Resource_Data' (Set => Set, In_Set => Elements (Set).Elements (E)); Element.Set (In_Pool); return; end if; end loop; -- The entry guard said we had an available resource raise Program_Error with "A resource should have been available"; end Get; ------------- -- Release -- ------------- procedure Release (In_Pool : in out Pool_Resource_Access; Set : Resource_Set) is begin -- Nothing to do after the pool itself has been freed. -- Normal reference counting will take place if Elements /= null then Trace (Me, "Released one resource"); In_Pool.Available := True; Elements (Set).Available := Elements (Set).Available + 1; else -- The pool has been destroyed and the resource is no longer used. -- Simply free it. Free (In_Pool.Element); Unchecked_Free (In_Pool); end if; end Release; ---------- -- Free -- ---------- procedure Free is R : Pool_Resource_Access; begin Increase_Indent (Me, "Global_Pool.Free"); if Elements /= null then for Set in Elements'Range loop if Elements (Set).Elements /= null then for E in Elements (Set).Elements'Range loop R := Elements (Set).Elements (E); if R /= null and then R.Available then Trace (Me, "Freeing a resource"); Free (R.Element); Unchecked_Free (R); elsif R /= null then Trace (Me, "One resource still in use, can't be freed"); end if; end loop; Free_Param (Elements (Set).Param); Unchecked_Free (Elements (Set).Elements); end if; end loop; Unchecked_Free (Elements); end if; Decrease_Indent (Me, "Done Global_Pool.Free"); end Free; end Pool; Global_Pool : Pool; -- a global pool -- This is task safe. ------------- -- Element -- ------------- function Element (Self : Resource) return access Element_Type is Enc : constant access Resource_Data := Get (Self).Element; begin Assert (Me, Enc /= null, "A wrapper should not exist without an element"); return Enc.In_Set.Element'Access; end Element; --------- -- Get -- --------- procedure Get (Self : out Resource'Class; Set : Resource_Set := Default_Set) is begin Global_Pool.Get (Set) (Self); end Get; -------------- -- Get_Weak -- -------------- function Get_Weak (Self : Resource'Class) return Weak_Resource is begin return Weak_Resource'(Ref => Self.Weak); end Get_Weak; --------- -- Get -- --------- procedure Get (Self : Weak_Resource; Res : out Resource) is begin Res.Set (Self.Ref); end Get; --------------- -- Was_Freed -- --------------- function Was_Freed (Self : Weak_Resource) return Boolean is begin return Pointers.Was_Freed (Self.Ref); end Was_Freed; ---------- -- Free -- ---------- procedure Free is begin Global_Pool.Free; end Free; ---------- -- Free -- ---------- procedure Free (Self : in out Resource_Data) is begin -- Call the user's callback before releasing into the pool, so that the -- resource doesn't get reused in the meantime. On_Release (Self.In_Set.Element); begin Global_Pool.Release (Self.In_Set, Self.Set); exception when E : Program_Error => Trace (Me, "Global pool was already finalized"); Trace (Me, E); end; end Free; ----------------- -- Set_Factory -- ----------------- procedure Set_Factory (Param : Factory_Param; Max_Elements : Positive; Set : Resource_Set := Default_Set) is begin Global_Pool.Set_Factory (Param, Max_Elements, Set); end Set_Factory; ----------------------- -- Get_Factory_Param -- ----------------------- function Get_Factory_Param (Set : Resource_Set := Default_Set) return access Factory_Param is begin return Global_Pool.Get_Factory_Param (Set); end Get_Factory_Param; ------------------ -- Get_Refcount -- ------------------ function Get_Refcount (Self : Resource) return Natural is begin return Pointers.Get_Refcount (Self); end Get_Refcount; end GNATCOLL.Pools; gnatcoll-core-21.0.0/src/gnatcoll-email-mailboxes.ads0000644000175000017500000003027013743647711022335 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2006-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package handles mailboxes that contain one or more email messages with Ada.Containers.Indefinite_Doubly_Linked_Lists; with Ada.Containers.Doubly_Linked_Lists; with Ada.Finalization; with GNATCOLL.Email.Parser; with GNAT.Strings; with GNATCOLL.VFS; package GNATCOLL.Email.Mailboxes is -- ??? Would be nice to have a function to write back a message in a -- mailbox (with proper message separators). --------------- -- Mailboxes -- --------------- type Mailbox is abstract tagged limited private; -- This type describes a mailbox, which contains several email messages in -- some defined format. See the children of this type for the various -- supported formats. -- This mailbox can be iterated: to get all messages, you would do the -- following: -- Box : Mbox; -- -- Open (Box, File_Contents) -- -- Curs : Cursor'Class := First (Box); -- while Has_Element (Curs) loop -- Get_Message (Curs, Box, Msg); -- if Msg /= Null_Message then -- -- test above is in case of parsing error -- ... -- Next (Curs, Box); -- end loop; type Message_Factory is access procedure (Str : String; Msg : out Message); -- Builds a message from a string. It should return Null_Message if the -- message could not be parsed. -- You can provide a different function if you simply want to get the text -- of all messages (for instance for a search function), and do not need to -- waste time actually parsing the message. type Cursor is abstract tagged private; -- An iterator over the contents of a mailbox function First (Self : Mailbox) return Cursor'Class is abstract; -- Return a cursor to iterator over all messages of the mailbox procedure Set_Parser (Self : in out Cursor; Factory : Message_Factory := Email.Parser.Parse'Access); -- Set the factory used to create the messages parsed from the mailbox. -- It can be used to limit which fields should be parsed, whether the body -- should be returned,... function Has_Element (Self : Cursor) return Boolean is abstract; -- True if Self points to a message in the mailbox, False if past the last -- message. procedure Get_Message (Self : in out Cursor; Box : Mailbox'Class; Msg : out Message) is abstract; -- Return the current message. -- If there is no such message or the message could not be parsed, returns -- Null_Message. -- The message is generated from the text representing the mailbox by -- calling the factory. procedure Next (Self : in out Cursor; Box : Mailbox'Class) is abstract; -- Moves to the next message in Self -------------------- -- Unix mailboxes -- -------------------- type Mbox is new Mailbox with private; -- This type describes a mail box in the traditional format used by Unix -- systems. Messages are appended one after another, separated by a blank -- line and a line starting with "From ". overriding function First (Self : Mbox) return Cursor'Class; -- Return an instance of Mbox_Cursor type Destructor is access procedure (S : in out GNAT.Strings.String_Access); -- Free the memory associated with the "Fp" parameter given to Open procedure Free_String (Str : in out GNAT.Strings.String_Access); procedure Open (Self : in out Mbox; Fp : access String; On_Close : Destructor := Free_String'Access); -- Initializes the internal data for the mailbox. This procedure must be -- called by the various *Open functions below, but doesn't need to be -- called by the user. -- No copy of Fp is made. On_Close (if defined) is called when the mbox no -- longer needs access to Fp. As a result, you can either give control over -- Fp to the mailbox (and leave the default value for On_Close), or keep -- control of the string, and pass null to On_Close. procedure Open (Self : in out Mbox; Filename : GNATCOLL.VFS.Virtual_File); -- Same as Open, but takes care of opening the file. -- If the file could not be open, Name_Error is raised. type Mbox_Cursor is new Cursor with private; overriding function Has_Element (Self : Mbox_Cursor) return Boolean; overriding procedure Next (Self : in out Mbox_Cursor; Box : Mailbox'Class); overriding procedure Get_Message (Self : in out Mbox_Cursor; Box : Mailbox'Class; Msg : out Message); -- See inherited documentation ------------------------- -- In-Memory mailboxes -- ------------------------- type Stored_Mailbox is new Mailbox with private; -- This type represents the contents of a mailbox in memory. All messages -- that are part of a file mailbox are read and kept in memory. This -- provides a convenient way to keep messages in memory while they are in -- use, and in particular provides ways to sort them. -- This type is limited since it would be costly to copy instances of a -- mailbox otherwise (duplicating all messages in memory). procedure Store (Self : out Stored_Mailbox; Box : in out Mailbox'Class; Factory : Message_Factory := Email.Parser.Parse'Access); procedure Store (Self : out Stored_Mailbox; Box : in out Mailbox'Class; Factory : Message_Factory := Email.Parser.Parse'Access; From : Cursor'Class); -- Parse a mailbox and store all its messages in memory. -- All messages previously in Self are kept. -- Box must already have been Open'ed. -- The second version allows you to skip messages if needed procedure Append (Self : in out Stored_Mailbox; Msg : Message); -- Appends a new message to Self. The current sorting order is not -- preserved, and you should call Sort_* again after you have added one or -- more messages. procedure Thread_Messages (Self : in out Stored_Mailbox); -- Sort all messages in Self by threads. This preserves the sort order. -- This does nothing if Self is already threaded. procedure Remove_Threads (Stored : in out Stored_Mailbox); -- Removing all threading information from Stored. The mailbox is no -- longer sorted as a result. function Is_Threaded (Self : Stored_Mailbox) return Boolean; -- Whether Self is sorted by threads procedure Sort_By_Date (Self : in out Stored_Mailbox); -- Sort all messages by Date. This preserves threading information if -- available. type Stored_Mailbox_Cursor is new Cursor with private; -- Iterate over the contents of a mailbox overriding function First (Self : Stored_Mailbox) return Cursor'Class; function First (Self : Stored_Mailbox; Recurse : Boolean) return Stored_Mailbox_Cursor'Class; -- Starts iteration over all elements in Self, in the order they were -- sorted. -- If Recurse is False and messages have been sorted by threads, this will -- only iterate over the root message of each thread. Use First_In_Thread -- to iterate recursively over each thread. Traversal is depth-first. -- If Recurse is True, then all messages will eventually be returned. -- The iterator becomes invalid when you call one of the Sort_* functions. -- The first version of First returns a cursor that iterates not -- recursively. function First_In_Thread (Self : Stored_Mailbox; Parent : Stored_Mailbox_Cursor'Class) return Stored_Mailbox_Cursor'Class; -- Return the first child of Msg in its thread. If the threads are -- organized as: -- Msg1 (thread level 1) -- |_ Msg1.1 (thread level 2) -- |_ Msg1.1.1 (thread level 3) -- |_ Msg1.2 (thread level 2) -- Msg2 (thread level 1); -- and Msg1 is passed in argument, then the iterator will return -- Msg1.1 and Msg1.2, not Msg1.1.1 nor Msg2. -- This function always returns an empty iterator if the mailbox is not -- sorted by threads. overriding procedure Next (Self : in out Stored_Mailbox_Cursor; Box : Mailbox'Class); -- See inherited documentation overriding procedure Get_Message (Self : in out Stored_Mailbox_Cursor; Box : Mailbox'Class; Msg : out Message); function Get_Thread_Level (Iter : Stored_Mailbox_Cursor) return Positive; -- Return the current message in the mailbox, or Null_Message if there are -- no more messages. See the small drawing above for the meaning of -- Thread_Level. If the mailbox has not been sorted by threads, the level -- is always 1. overriding function Has_Element (Self : Stored_Mailbox_Cursor) return Boolean; -- Whether calling Next on Iter will return a Message private type Mailbox is abstract new Ada.Finalization.Limited_Controlled with record null; end record; type Cursor is abstract tagged record Factory : Message_Factory := Email.Parser.Parse'Access; end record; type Mbox_Cursor is new Cursor with record Start, Stop : Integer; Max : Integer; Current : Message; -- Cache the current message end record; overriding procedure Finalize (Self : in out Mailbox); pragma Finalize_Storage_Only (Mailbox); type Mbox is new Mailbox with record Fp : GNAT.Strings.String_Access; On_Close : Destructor; Previous_Line_Empty : Boolean := True; end record; overriding procedure Finalize (Self : in out Mbox); -- See inherited documentation type Abstract_Message_Info is abstract tagged record Msg : Message; end record; package Message_Info_List is new Ada.Containers.Indefinite_Doubly_Linked_Lists (Abstract_Message_Info'Class); type Message_Info is new Abstract_Message_Info with record Children : Message_Info_List.List; end record; type Sort_Order is (Sort_None, Sort_Date); type Stored_Mailbox is new Mailbox with record Messages : Message_Info_List.List; -- Contains Message_Info Sorted_By : Sort_Order := Sort_None; Threaded : Boolean := False; end record; package Cursor_List is new Ada.Containers.Doubly_Linked_Lists (Message_Info_List.Cursor, Message_Info_List."="); type Stored_Mailbox_Cursor is new Cursor with record Cursors : Cursor_List.List; Recurse : Boolean; Thread_Level : Integer; end record; -- If the specified thread level is 0, all messages are returned. -- Otherwise, only the messages at the right level. end GNATCOLL.Email.Mailboxes; gnatcoll-core-21.0.0/src/gnatcoll-terminal.ads0000644000175000017500000001673713661715457021116 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2014-2017, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This package provides a number of cross-platform subprograms to control -- output in terminals, in particular colors. -- -- On Windows, color sequences are either set using the standard WIN32 codes, -- or if the package ANSICON (https://github.com/adoxa/ansicon/) is running it -- will use the standard ANSI sequences. with Ada.Text_IO; package GNATCOLL.Terminal is type Terminal_Info is tagged private; type Terminal_Info_Access is access all Terminal_Info'Class; -- Information about a terminal on which we output. -- This structure does not encapsulate the terminal itself, which is a -- limited type. -- By default, this is configured without support for colors. It is thus -- recommended to first call Init before you use this type. -- This type is almost always used in conjunction with a File_Type, which -- is where text is actually output. The properties of that File_Type are -- queried and cached in the Terminal_Info. ------------ -- Colors -- ------------ type Supports_Color is (Yes, No, Auto); procedure Init_For_Stdout (Self : in out Terminal_Info; Colors : Supports_Color := Auto); procedure Init_For_Stderr (Self : in out Terminal_Info; Colors : Supports_Color := Auto); procedure Init_For_File (Self : in out Terminal_Info; Colors : Supports_Color := Auto); -- Checks whether the terminal supports colors. By default, automatic -- detection is attempted, but this can be overridden by the use of the -- Colors parameter. -- The three variants depend on which type of terminal you are outputting -- to. Unfortunately, the type Ada.Text_IO.File_Type is opaque and it is -- not possible to check what is applies to, or what are the properties of -- the underling file handle. function Has_Colors (Self : Terminal_Info) return Boolean; -- Whether the terminals supports colors. function Has_ANSI_Colors (Self : Terminal_Info) return Boolean; -- Whether the terminal supports ANSI escape sequences for colors. -- On Windows, it is possible for a terminal to support colors, but not -- ANSI sequences. This package will take care of doing the appropriate -- system calls to setup colors, but if you want to directly output -- ANSI sequences that will not work. type ANSI_Color is (Unchanged, Black, Red, Green, Yellow, Blue, Magenta, Cyan, Grey, Reset); -- The colors that can be output in a terminal (ANSI definitions). The -- actual color that the user will see might be different, since a terminal -- might associate a different color to the same escape sequence. type ANSI_Style is (Unchanged, Bright, Dim, Normal, Reset_All); -- The style for the text. Some styles are not supported on some -- terminals, like Dim on the Windows console. procedure Set_Color (Self : in out Terminal_Info; Term : Ada.Text_IO.File_Type := Ada.Text_IO.Standard_Output; Foreground : ANSI_Color := Unchanged; Background : ANSI_Color := Unchanged; Style : ANSI_Style := Unchanged); -- Change the colors that will be used for subsequent output on the -- terminal. -- This procedure has no effect if Has_Colors returns False. -- In general, it is not recommended to output colors to files, so you -- should not use Set_Color in such a context. procedure Set_Fg (Self : in out Terminal_Info; Color : ANSI_Color; Term : Ada.Text_IO.File_Type := Ada.Text_IO.Standard_Output); procedure Set_Bg (Self : in out Terminal_Info; Color : ANSI_Color; Term : Ada.Text_IO.File_Type := Ada.Text_IO.Standard_Output); procedure Set_Style (Self : in out Terminal_Info; Style : ANSI_Style; Term : Ada.Text_IO.File_Type := Ada.Text_IO.Standard_Output); -- Override specific colors. ------------- -- Cursors -- ------------- procedure Beginning_Of_Line (Self : in out Terminal_Info); -- Move the cursor back to the beginning of the line. -- This has no impact on files, only in interactive terminals. procedure Clear_To_End_Of_Line (Self : in out Terminal_Info); -- Delete from the cursor position to the end of line function Get_Width (Self : Terminal_Info) return Integer; -- Return the width of the terminal, or -1 if that width is either -- unknown or does not apply (as is the case for files for instance). ----------- -- Utils -- ----------- type Full_Style is record Fg : ANSI_Color := Unchanged; Bg : ANSI_Color := Unchanged; Style : ANSI_Style := Unchanged; end record; -- A convenient record to group all style-related attributes function Get_ANSI_Sequence (Style : Full_Style) return String; -- Append the ANSI escape sequence representing the style. -- Note that these sequences are not supported by all terminals, see -- Has_ANSI_Colors. private type Color_Sequence_Type is (Unsupported, ANSI_Sequences, WIN32_Sequences); type FD_Type is (Stdout, Stderr, File); -- What type of file descriptor the terminal_info applies to. type Terminal_Info is tagged record Colors : Color_Sequence_Type := Unsupported; Fore : ANSI_Color := Black; Back : ANSI_Color := Grey; Style : ANSI_Style := Normal; -- Current attributes (on Windows, all three must be changed at the -- same time) Default_Fore : ANSI_Color := Black; Default_Back : ANSI_Color := Grey; Default_Style : ANSI_Style := Normal; -- Default windows attributes (computed in Init) FD : FD_Type := Stdout; -- Whether the associated terminal is stdout (windows only) end record; end GNATCOLL.Terminal; gnatcoll-core-21.0.0/src/gnatcoll-coders.adb0000644000175000017500000001071413661715457020526 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ package body GNATCOLL.Coders is ----------- -- Flush -- ----------- procedure Flush (Coder : in out Coder_Interface'Class; Out_Data : out Stream_Element_Array; Out_Last : out Stream_Element_Offset; Flush : Flush_Mode) is No_Data : constant Stream_Element_Array := (1 .. 0 => 0); Last : Stream_Element_Offset; begin Transcode (Coder, No_Data, Last, Out_Data, Out_Last, Flush); end Flush; ----------- -- Write -- ----------- procedure Write (Coder : in out Coder_Interface'Class; Item : Stream_Element_Array; Flush : Flush_Mode := No_Flush) is Buffer : Stream_Element_Array (1 .. Buffer_Size); In_Last : Stream_Element_Offset; Out_Last : Stream_Element_Offset; In_First : Stream_Element_Offset := Item'First; begin if Item'Length = 0 and Flush = No_Flush then return; end if; loop Transcode (Coder => Coder, In_Data => Item (In_First .. Item'Last), In_Last => In_Last, Out_Data => Buffer, Out_Last => Out_Last, Flush => Flush); if Out_Last >= Buffer'First then Write (Buffer (1 .. Out_Last)); end if; exit when In_Last = Item'Last or Finished (Coder); In_First := In_Last + 1; end loop; end Write; ---------- -- Read -- ---------- procedure Read (Coder : in out Coder_Interface'Class; Item : out Stream_Element_Array; Last : out Stream_Element_Offset; Flush : Flush_Mode := No_Flush) is In_Last : Stream_Element_Offset; Item_First : Stream_Element_Offset := Item'First; V_Flush : Flush_Mode := Flush; begin pragma Assert (Rest_First in Buffer'First .. Buffer'Last + 1); pragma Assert (Rest_Last in Buffer'First - 1 .. Buffer'Last); loop if Rest_Last = Buffer'First - 1 then V_Flush := Finish; elsif Rest_First > Rest_Last then Read (Buffer, Rest_Last); Rest_First := Buffer'First; if Rest_Last < Buffer'First then V_Flush := Finish; end if; end if; Transcode (Coder => Coder, In_Data => Buffer (Rest_First .. Rest_Last), In_Last => In_Last, Out_Data => Item (Item_First .. Item'Last), Out_Last => Last, Flush => V_Flush); Rest_First := In_Last + 1; exit when Finished (Coder) or else Last = Item'Last or else (Last >= Item'First and then Allow_Read_Some); Item_First := Last + 1; end loop; end Read; end GNATCOLL.Coders; gnatcoll-core-21.0.0/version_information0000644000175000017500000000000413661715457020213 0ustar nicolasnicolas0.0 gnatcoll-core-21.0.0/gnat_debug.adc0000644000175000017500000000003313661715457016750 0ustar nicolasnicolaspragma Initialize_Scalars; gnatcoll-core-21.0.0/COPYING30000644000175000017500000010451313661715457015326 0ustar nicolasnicolas GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . gnatcoll-core-21.0.0/distrib/0000755000175000017500000000000013661715457015644 5ustar nicolasnicolasgnatcoll-core-21.0.0/distrib/gen_gps.py0000644000175000017500000000326313661715457017644 0ustar nicolasnicolas#!/usr/bin/env python import os import os.path import re pkg_re = re.compile("^(private)?\s*package\s*(\S+)") def recursive_ls(dir): """Return the list of ads files in dir and its subdirs""" result = set() for f in os.listdir(dir): if f.endswith(".ads") \ and f.startswith("gnatcoll-"): private = False pkg = "" for l in file(os.path.join(dir, f)).readlines(): m = pkg_re.search(l) if m: private = m.group(1) pkg = m.group(2) break if not private: result.add((pkg, os.path.splitext(f)[0])) elif os.path.isdir(os.path.join(dir, f)): result = result.union(recursive_ls(os.path.join(dir, f))) return result list = recursive_ls("../src") out = file("gnatcoll/runtime.py", "wb") out.write("""XML = r''' """) for pkg, f in sorted(list): if '__' in f: # An internal package with a specific naming scheme continue menu = pkg.replace(".", "/").replace("_", "__") # Do we have a submenu ? in_front = False for pkg2, b in list: if b.startswith(f + "-"): item = menu[menu.rfind("/") + 1:] menu = menu + "/<" + item + ">" break out.write(""" Editor.edit "%(file)s.ads" %(package)s /Help/GNAT Runtime/%(menu)s GNAT Components Collection """ % {"file": f, "menu": menu, "package": pkg}) out.write("""''' import GPS GPS.parse_xml(XML) """) out.close() gnatcoll-core-21.0.0/distrib/gnatcoll/0000755000175000017500000000000013661715457017447 5ustar nicolasnicolasgnatcoll-core-21.0.0/distrib/gnatcoll/__init__.py0000644000175000017500000000073213661715457021562 0ustar nicolasnicolas""" This file makes the GNATCOLL documentation available from GPS """ import GPS from . import runtime XML = r""" share/doc/gnatcoll html/index.html Gnat Reusable Components User's Guide GNAT /Help/Gnat Components/Gnat Components User's Guide """ GPS.parse_xml(XML) gnatcoll-core-21.0.0/distrib/gnatcoll/runtime.py0000644000175000017500000006065113661715457021514 0ustar nicolasnicolasXML = r''' Editor.edit "gnatcoll-format_columns_vertical.ads" /Help/GNAT Runtime/ GNAT Components Collection Editor.edit "gnatcoll-format_columns_vertical_xstring.ads" /Help/GNAT Runtime/ GNAT Components Collection Editor.edit "gnatcoll-any_types.ads" GNATCOLL.Any_Types /Help/GNAT Runtime/GNATCOLL/Any__Types/<Any__Types> GNAT Components Collection Editor.edit "gnatcoll-any_types-python.ads" GNATCOLL.Any_Types.Python /Help/GNAT Runtime/GNATCOLL/Any__Types/Python GNAT Components Collection Editor.edit "gnatcoll-arg_lists.ads" GNATCOLL.Arg_Lists /Help/GNAT Runtime/GNATCOLL/Arg__Lists GNAT Components Collection Editor.edit "gnatcoll-asserts.ads" GNATCOLL.Asserts /Help/GNAT Runtime/GNATCOLL/Asserts GNAT Components Collection Editor.edit "gnatcoll-atomic.ads" GNATCOLL.Atomic /Help/GNAT Runtime/GNATCOLL/Atomic GNAT Components Collection Editor.edit "gnatcoll-boyer_moore.ads" GNATCOLL.Boyer_Moore /Help/GNAT Runtime/GNATCOLL/Boyer__Moore GNAT Components Collection Editor.edit "gnatcoll-config.ads" GNATCOLL.Config /Help/GNAT Runtime/GNATCOLL/Config GNAT Components Collection Editor.edit "gnatcoll-email.ads" GNATCOLL.Email /Help/GNAT Runtime/GNATCOLL/Email/<Email> GNAT Components Collection Editor.edit "gnatcoll-email-mailboxes.ads" GNATCOLL.Email.Mailboxes /Help/GNAT Runtime/GNATCOLL/Email/Mailboxes GNAT Components Collection Editor.edit "gnatcoll-email-parser.ads" GNATCOLL.Email.Parser /Help/GNAT Runtime/GNATCOLL/Email/Parser GNAT Components Collection Editor.edit "gnatcoll-email-utils.ads" GNATCOLL.Email.Utils /Help/GNAT Runtime/GNATCOLL/Email/Utils GNAT Components Collection Editor.edit "gnatcoll-formatters.ads" GNATCOLL.Formatters /Help/GNAT Runtime/GNATCOLL/Formatters GNAT Components Collection Editor.edit "gnatcoll-gmp.ads" GNATCOLL.GMP /Help/GNAT Runtime/GNATCOLL/GMP/<GMP> GNAT Components Collection Editor.edit "gnatcoll-gmp-integers.ads" GNATCOLL.GMP.Integers /Help/GNAT Runtime/GNATCOLL/GMP/Integers/<Integers> GNAT Components Collection Editor.edit "gnatcoll-gmp-integers-io.ads" GNATCOLL.GMP.Integers.IO /Help/GNAT Runtime/GNATCOLL/GMP/Integers/IO GNAT Components Collection Editor.edit "gnatcoll-gmp-integers-misc.ads" GNATCOLL.GMP.Integers.Misc /Help/GNAT Runtime/GNATCOLL/GMP/Integers/Misc GNAT Components Collection Editor.edit "gnatcoll-gmp-integers-number_theoretic.ads" GNATCOLL.GMP.Integers.Number_Theoretic /Help/GNAT Runtime/GNATCOLL/GMP/Integers/Number__Theoretic GNAT Components Collection Editor.edit "gnatcoll-gmp-integers-random.ads" GNATCOLL.GMP.Integers.Random /Help/GNAT Runtime/GNATCOLL/GMP/Integers/Random GNAT Components Collection Editor.edit "gnatcoll-gmp-integers-root_extraction.ads" GNATCOLL.GMP.Integers.Root_Extraction /Help/GNAT Runtime/GNATCOLL/GMP/Integers/Root__Extraction GNAT Components Collection Editor.edit "gnatcoll-gmp-lib.ads" GNATCOLL.GMP.Lib /Help/GNAT Runtime/GNATCOLL/GMP/Lib GNAT Components Collection Editor.edit "gnatcoll-gmp-random_state.ads" GNATCOLL.GMP.Random_State /Help/GNAT Runtime/GNATCOLL/GMP/Random__State GNAT Components Collection Editor.edit "gnatcoll-geometry.ads" GNATCOLL.Geometry /Help/GNAT Runtime/GNATCOLL/Geometry GNAT Components Collection Editor.edit "gnatcoll-io-native.ads" GNATCOLL.IO.Native /Help/GNAT Runtime/GNATCOLL/IO/Native GNAT Components Collection Editor.edit "gnatcoll-io-remote.ads" GNATCOLL.IO.Remote /Help/GNAT Runtime/GNATCOLL/IO/Remote/<Remote> GNAT Components Collection Editor.edit "gnatcoll-io-remote-unix.ads" GNATCOLL.IO.Remote.Unix /Help/GNAT Runtime/GNATCOLL/IO/Remote/Unix GNAT Components Collection Editor.edit "gnatcoll-io-remote-windows.ads" GNATCOLL.IO.Remote.Windows /Help/GNAT Runtime/GNATCOLL/IO/Remote/Windows GNAT Components Collection Editor.edit "gnatcoll-iconv.ads" GNATCOLL.Iconv /Help/GNAT Runtime/GNATCOLL/Iconv GNAT Components Collection Editor.edit "gnatcoll-json.ads" GNATCOLL.JSON /Help/GNAT Runtime/GNATCOLL/JSON GNAT Components Collection Editor.edit "gnatcoll-memory.ads" GNATCOLL.Memory /Help/GNAT Runtime/GNATCOLL/Memory GNAT Components Collection Editor.edit "gnatcoll-mmap.ads" GNATCOLL.Mmap /Help/GNAT Runtime/GNATCOLL/Mmap/<Mmap> GNAT Components Collection Editor.edit "gnatcoll-paragraph_filling.ads" GNATCOLL.Paragraph_Filling /Help/GNAT Runtime/GNATCOLL/Paragraph__Filling GNAT Components Collection Editor.edit "gnatcoll-pools.ads" GNATCOLL.Pools /Help/GNAT Runtime/GNATCOLL/Pools GNAT Components Collection Editor.edit "gnatcoll-projects.ads" GNATCOLL.Projects /Help/GNAT Runtime/GNATCOLL/Projects/<Projects> GNAT Components Collection Editor.edit "gnatcoll-projects-aux.ads" GNATCOLL.Projects.Aux /Help/GNAT Runtime/GNATCOLL/Projects/Aux GNAT Components Collection Editor.edit "gnatcoll-promises.ads" GNATCOLL.Promises /Help/GNAT Runtime/GNATCOLL/Promises GNAT Components Collection Editor.edit "gnatcoll-python.ads" GNATCOLL.Python /Help/GNAT Runtime/GNATCOLL/Python GNAT Components Collection Editor.edit "gnatcoll-ravenscar.ads" GNATCOLL.Ravenscar /Help/GNAT Runtime/GNATCOLL/Ravenscar/<Ravenscar> GNAT Components Collection Editor.edit "gnatcoll-ravenscar-multiple_queue_cyclic_server.ads" GNATCOLL.Ravenscar.Multiple_Queue_Cyclic_Server /Help/GNAT Runtime/GNATCOLL/Ravenscar/Multiple__Queue__Cyclic__Server GNAT Components Collection Editor.edit "gnatcoll-ravenscar-multiple_queue_sporadic_server.ads" GNATCOLL.Ravenscar.Multiple_Queue_Sporadic_Server /Help/GNAT Runtime/GNATCOLL/Ravenscar/Multiple__Queue__Sporadic__Server GNAT Components Collection Editor.edit "gnatcoll-ravenscar-simple_cyclic_task.ads" GNATCOLL.Ravenscar.Simple_Cyclic_Task /Help/GNAT Runtime/GNATCOLL/Ravenscar/Simple__Cyclic__Task GNAT Components Collection Editor.edit "gnatcoll-ravenscar-simple_sporadic_task.ads" GNATCOLL.Ravenscar.Simple_Sporadic_Task /Help/GNAT Runtime/GNATCOLL/Ravenscar/Simple__Sporadic__Task GNAT Components Collection Editor.edit "gnatcoll-ravenscar-sporadic_server.ads" GNATCOLL.Ravenscar.Sporadic_Server /Help/GNAT Runtime/GNATCOLL/Ravenscar/Sporadic__Server GNAT Components Collection Editor.edit "gnatcoll-ravenscar-sporadic_server_with_callback.ads" GNATCOLL.Ravenscar.Sporadic_Server_With_Callback /Help/GNAT Runtime/GNATCOLL/Ravenscar/Sporadic__Server__With__Callback GNAT Components Collection Editor.edit "gnatcoll-ravenscar-timed_out_sporadic_server.ads" GNATCOLL.Ravenscar.Timed_Out_Sporadic_Server /Help/GNAT Runtime/GNATCOLL/Ravenscar/Timed__Out__Sporadic__Server GNAT Components Collection Editor.edit "gnatcoll-ravenscar-timers.ads" GNATCOLL.Ravenscar.Timers /Help/GNAT Runtime/GNATCOLL/Ravenscar/Timers/<Timers> GNAT Components Collection Editor.edit "gnatcoll-ravenscar-timers-one_shot_timer.ads" GNATCOLL.Ravenscar.Timers.One_Shot_Timer /Help/GNAT Runtime/GNATCOLL/Ravenscar/Timers/One__Shot__Timer GNAT Components Collection Editor.edit "gnatcoll-ravenscar-utils.ads" GNATCOLL.Ravenscar.Utils /Help/GNAT Runtime/GNATCOLL/Ravenscar/Utils GNAT Components Collection Editor.edit "gnatcoll-readline.ads" GNATCOLL.Readline /Help/GNAT Runtime/GNATCOLL/Readline GNAT Components Collection Editor.edit "gnatcoll-refcount.ads" GNATCOLL.Refcount /Help/GNAT Runtime/GNATCOLL/Refcount/<Refcount> GNAT Components Collection Editor.edit "gnatcoll-refcount-weakref.ads" GNATCOLL.Refcount.Weakref /Help/GNAT Runtime/GNATCOLL/Refcount/Weakref GNAT Components Collection Editor.edit "gnatcoll-remote.ads" GNATCOLL.Remote /Help/GNAT Runtime/GNATCOLL/Remote/<Remote> GNAT Components Collection Editor.edit "gnatcoll-remote-db.ads" GNATCOLL.Remote.Db /Help/GNAT Runtime/GNATCOLL/Remote/Db GNAT Components Collection Editor.edit "gnatcoll-sql.ads" GNATCOLL.SQL /Help/GNAT Runtime/GNATCOLL/SQL/<SQL> GNAT Components Collection Editor.edit "gnatcoll-sql-exec.ads" GNATCOLL.SQL.Exec /Help/GNAT Runtime/GNATCOLL/SQL/Exec/<Exec> GNAT Components Collection Editor.edit "gnatcoll-sql-exec-tasking.ads" GNATCOLL.SQL.Exec.Tasking /Help/GNAT Runtime/GNATCOLL/SQL/Exec/Tasking GNAT Components Collection Editor.edit "gnatcoll-sql-inspect.ads" GNATCOLL.SQL.Inspect /Help/GNAT Runtime/GNATCOLL/SQL/Inspect GNAT Components Collection Editor.edit "gnatcoll-sql-orm.ads" GNATCOLL.SQL.Orm /Help/GNAT Runtime/GNATCOLL/SQL/Orm/<Orm> GNAT Components Collection Editor.edit "gnatcoll-sql-orm-impl.ads" GNATCOLL.SQL.Orm.Impl /Help/GNAT Runtime/GNATCOLL/SQL/Orm/Impl GNAT Components Collection Editor.edit "gnatcoll-sql-postgres.ads" GNATCOLL.SQL.Postgres /Help/GNAT Runtime/GNATCOLL/SQL/Postgres/<Postgres> GNAT Components Collection Editor.edit "gnatcoll-sql-postgres-gnade.ads" GNATCOLL.SQL.Postgres.Gnade /Help/GNAT Runtime/GNATCOLL/SQL/Postgres/Gnade GNAT Components Collection Editor.edit "gnatcoll-sql-ranges.ads" GNATCOLL.SQL.Ranges /Help/GNAT Runtime/GNATCOLL/SQL/Ranges GNAT Components Collection Editor.edit "gnatcoll-sql-sessions.ads" GNATCOLL.SQL.Sessions /Help/GNAT Runtime/GNATCOLL/SQL/Sessions GNAT Components Collection Editor.edit "gnatcoll-sql-sqlite.ads" GNATCOLL.SQL.Sqlite /Help/GNAT Runtime/GNATCOLL/SQL/Sqlite/<Sqlite> GNAT Components Collection Editor.edit "gnatcoll-sql-sqlite-gnade.ads" GNATCOLL.SQL.Sqlite.Gnade /Help/GNAT Runtime/GNATCOLL/SQL/Sqlite/Gnade GNAT Components Collection Editor.edit "gnatcoll-sql_fields.ads" GNATCOLL.SQL_Fields /Help/GNAT Runtime/GNATCOLL/SQL__Fields GNAT Components Collection Editor.edit "gnatcoll-sql_impl.ads" GNATCOLL.SQL_Impl /Help/GNAT Runtime/GNATCOLL/SQL__Impl GNAT Components Collection Editor.edit "gnatcoll-scripts.ads" GNATCOLL.Scripts /Help/GNAT Runtime/GNATCOLL/Scripts/<Scripts> GNAT Components Collection Editor.edit "gnatcoll-scripts-files.ads" GNATCOLL.Scripts.Files /Help/GNAT Runtime/GNATCOLL/Scripts/Files GNAT Components Collection Editor.edit "gnatcoll-scripts-impl.ads" GNATCOLL.Scripts.Impl /Help/GNAT Runtime/GNATCOLL/Scripts/Impl GNAT Components Collection Editor.edit "gnatcoll-scripts-projects.ads" GNATCOLL.Scripts.Projects /Help/GNAT Runtime/GNATCOLL/Scripts/Projects GNAT Components Collection Editor.edit "gnatcoll-scripts-python.ads" GNATCOLL.Scripts.Python /Help/GNAT Runtime/GNATCOLL/Scripts/Python GNAT Components Collection Editor.edit "gnatcoll-scripts-shell.ads" GNATCOLL.Scripts.Shell /Help/GNAT Runtime/GNATCOLL/Scripts/Shell GNAT Components Collection Editor.edit "gnatcoll-scripts-utils.ads" GNATCOLL.Scripts.Utils /Help/GNAT Runtime/GNATCOLL/Scripts/Utils GNAT Components Collection Editor.edit "gnatcoll-storage_pools.ads" GNATCOLL.Storage_Pools /Help/GNAT Runtime/GNATCOLL/Storage__Pools/<Storage__Pools> GNAT Components Collection Editor.edit "gnatcoll-storage_pools-alignment.ads" GNATCOLL.Storage_Pools.Alignment /Help/GNAT Runtime/GNATCOLL/Storage__Pools/Alignment GNAT Components Collection Editor.edit "gnatcoll-storage_pools-headers.ads" GNATCOLL.Storage_Pools.Headers /Help/GNAT Runtime/GNATCOLL/Storage__Pools/Headers GNAT Components Collection Editor.edit "gnatcoll-strings.ads" GNATCOLL.Strings /Help/GNAT Runtime/GNATCOLL/Strings GNAT Components Collection Editor.edit "gnatcoll-strings_impl.ads" GNATCOLL.Strings_Impl /Help/GNAT Runtime/GNATCOLL/Strings__Impl GNAT Components Collection Editor.edit "gnatcoll-symbols.ads" GNATCOLL.Symbols /Help/GNAT Runtime/GNATCOLL/Symbols GNAT Components Collection Editor.edit "gnatcoll-templates.ads" GNATCOLL.Templates /Help/GNAT Runtime/GNATCOLL/Templates GNAT Components Collection Editor.edit "gnatcoll-terminal.ads" GNATCOLL.Terminal /Help/GNAT Runtime/GNATCOLL/Terminal GNAT Components Collection Editor.edit "gnatcoll-traces.ads" GNATCOLL.Traces /Help/GNAT Runtime/GNATCOLL/Traces/<Traces> GNAT Components Collection Editor.edit "gnatcoll-traces-syslog.ads" GNATCOLL.Traces.Syslog /Help/GNAT Runtime/GNATCOLL/Traces/Syslog GNAT Components Collection Editor.edit "gnatcoll-tribooleans.ads" GNATCOLL.Tribooleans /Help/GNAT Runtime/GNATCOLL/Tribooleans GNAT Components Collection Editor.edit "gnatcoll-utils.ads" GNATCOLL.Utils /Help/GNAT Runtime/GNATCOLL/Utils GNAT Components Collection Editor.edit "gnatcoll-vfs.ads" GNATCOLL.VFS /Help/GNAT Runtime/GNATCOLL/VFS GNAT Components Collection Editor.edit "gnatcoll-vfs_types.ads" GNATCOLL.VFS_Types /Help/GNAT Runtime/GNATCOLL/VFS__Types GNAT Components Collection Editor.edit "gnatcoll-vfs_utils.ads" GNATCOLL.VFS_Utils /Help/GNAT Runtime/GNATCOLL/VFS__Utils GNAT Components Collection Editor.edit "gnatcoll-xref.ads" GNATCOLL.Xref /Help/GNAT Runtime/GNATCOLL/Xref/<Xref> GNAT Components Collection Editor.edit "gnatcoll-xref-database.ads" GNATCOLL.Xref.Database /Help/GNAT Runtime/GNATCOLL/Xref/Database GNAT Components Collection Editor.edit "gnatcoll-xref-database_names.ads" GNATCOLL.Xref.Database_Names /Help/GNAT Runtime/GNATCOLL/Xref/Database__Names GNAT Components Collection ''' import GPS GPS.parse_xml(XML) gnatcoll-core-21.0.0/.gitattributes0000644000175000017500000000146013743645322017072 0ustar nicolasnicolasREADME.md no-precommit-check testsuite/*/* no-precommit-check testsuite/*/*/* no-precommit-check testsuite/*/*/*/* no-precommit-check testsuite/*/*/*/*/* no-precommit-check # Third-party package src/getRSS.c no-precommit-check src/sqlite/amalgamation/* no-precommit-check src/dborm.py no-precommit-check src/xref.generated/* no-precommit-check distrib/gnatcoll/runtime.py no-precommit-check # ??? Workaround bug in style checker, which complains that # Finalization_Size is an unrecognized attribute src/gnatcoll-storage_pools-headers.adb no-precommit-check gnatcoll-core-21.0.0/gnatcoll.gpr0000644000175000017500000001505413743647711016524 0ustar nicolasnicolas------------------------------------------------------------------------------ -- G N A T C O L L -- -- -- -- Copyright (C) 2015-2020, AdaCore -- -- -- -- This library is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ with "gpr"; project GnatColl is Version := External ("GNATCOLL_VERSION", "0.0"); Name := "gnatcoll"; type Yes_No is ("yes", "no"); Mmap : Yes_No := External ("GNATCOLL_MMAP", "yes"); Madvise : Yes_No := External ("GNATCOLL_MADVISE", "yes"); type Build_Type is ("DEBUG", "PROD"); Build : Build_Type := External ("GNATCOLL_BUILD_MODE", External ("BUILD", "DEBUG")); type Library_Type_Type is ("relocatable", "static", "static-pic"); Library_Type : Library_Type_Type := External ("LIBRARY_TYPE", "static"); for Source_Dirs use ("src", "src/paragraph_filling"); for Library_Kind use Library_Type; for Object_Dir use "obj/gnatcoll/" & Project'Library_Kind; for Library_Dir use "lib/gnatcoll/" & Project'Library_Kind; for Library_Name use Name; for Languages use ("Ada", "C"); type OS_Kind is ("windows", "unix", "osx"); OS : OS_Kind := External ("GNATCOLL_OS", "unix"); Extra_Switches := ("-DATOMIC_INTRINSICS"); Extra_Libs := (); case OS is when "windows" => Extra_Libs := ("-lpsapi"); -- For gnatcoll.memory when others => case Mmap is when "yes" => Extra_Switches := ("-DHAVE_MMAP"); case Madvise is when "yes" => Extra_Switches := Extra_Switches & ("-DHAVE_MADVISE"); when "no" => null; end case; when others => null; end case; end case; So_Ext := ""; case OS is when "windows" => So_Ext := ".dll"; when "osx" => So_Ext := ".dylib"; when others => So_Ext := ".so"; end case; for Library_Version use "lib" & Name & So_Ext & "." & Version; case Library_Type is when "relocatable" => for Library_Options use Extra_Libs; -- Put user options in front, for options like --as-needed. for Leading_Library_Options use External_As_List ("LDFLAGS", " "); when others => null; end case; package Compiler is case Build is when "DEBUG" => for Switches ("Ada") use ("-g", "-O0", "-gnata", "-gnatVa", "-gnatQ", "-gnaty", "-gnateE", "-gnatwaCJe", "-fstack-check"); for Switches ("C") use ("-g", "-Wunreachable-code"); when "PROD" => -- Do not use -gnatwe for production mode for Switches ("Ada") use ("-O2", "-gnatn", "-gnatwaCJ"); for Switches ("C") use ("-O2", "-Wunreachable-code"); end case; for Switches ("gnatcoll_support.c") use Compiler'Switches ("C") & Extra_Switches; -- Give user flags the last word. for Switches ("Ada") use Compiler'Switches ("Ada") & External_As_List ("ADAFLAGS", " "); for Switches ("C") use Compiler'Switches ("C") & External_As_List ("CFLAGS", " ") & External_As_List ("CPPFLAGS", " "); end Compiler; package Binder is case Build is when "DEBUG" => for Switches ("Ada") use ("-E"); when "PROD" => null; end case; end Binder; package Builder is case Build is when "DEBUG" => for Global_Configuration_Pragmas use "gnat_debug.adc"; when "PROD" => null; end case; end Builder; package Ide is for VCS_Kind use "Git"; end Ide; package Naming is for Specification ("GNATCOLL.OS.Constants") use "gnatcoll-os-constants__" & OS & ".ads"; case OS is when "unix" | "osx" => for Specification ("GNATCOLL.Mmap.System") use "gnatcoll-mmap-system__unix.ads"; for Implementation ("GNATCOLL.Mmap.System") use "gnatcoll-mmap-system__unix.adb"; for Implementation ("GNATCOLL.IO.Native.Codec") use "gnatcoll-io-native-codec__unix.adb"; for Implementation ("GNATCOLL.Plugins") use "gnatcoll-plugins__unix.adb"; when "windows" => for Specification ("GNATCOLL.Mmap.System") use "gnatcoll-mmap-system__win32.ads"; for Implementation ("GNATCOLL.Mmap.System") use "gnatcoll-mmap-system__win32.adb"; for Implementation ("GNATCOLL.IO.Native.Codec") use "gnatcoll-io-native-codec__win32.adb"; for Implementation ("GNATCOLL.Plugins") use "gnatcoll-plugins__windows.adb"; end case; end Naming; package Linker is for Linker_Options use Extra_Libs; end Linker; package Install is for Artifacts ("share/examples/gnatcoll") use ("examples/*"); for Artifacts ("share/doc/gnatcoll/html") use ("docs/_build/html"); for Artifacts ("share/doc/gnatcoll") use ("docs/_build/latex/GNATColl.pdf"); end Install; end GnatColl; gnatcoll-core-21.0.0/CONTRIBUTING.md0000644000175000017500000000232213661715457016434 0ustar nicolasnicolasContributing to GNATcoll ======================== Thank you for taking the time to contribute! If this is your first contribution, we invite you to read our [list of guidelines](https://github.com/AdaCore/contributing-howto), common to all AdaCore repositories. Below are specific guidelines to contribute to GNATcoll. Coding style ------------ Please follow [GNAT's coding style](https://gcc.gnu.org/onlinedocs/gnat-style/) for Ada code, and [PEP8](https://www.python.org/dev/peps/pep-0008/) for Python code. Commits ------- Organize your work into separated, atomic commits. A commit should ideally contain the single smallest unit of change possible without breaking anything. A change should include any tests that were added or modified for it. Testing ------- Every change you add to the code should be tested. If this is a bug fix, add regression test(s). If it's a change of functionality, add functional test(s). The available tests should provide 100% coverage of the lines added or modified by the change; if this is not achievable, provide justification why some lines can't be covered (such as defensive code, etc). Please refer to [GNATcoll testsuite documentation](testsuite/README.md) for technical details. gnatcoll-core-21.0.0/.gitreview0000644000175000017500000000013213743647711016204 0ustar nicolasnicolas[gerrit] host = git.adacore.com port = 29418 project = gnatcoll-core defaultbranch = 21.1 gnatcoll-core-21.0.0/docs/0000755000175000017500000000000013661715457015134 5ustar nicolasnicolasgnatcoll-core-21.0.0/docs/strings.rst0000644000175000017500000003022613661715457017362 0ustar nicolasnicolas.. highlight:: ada ************************************* **Strings**: high-performance strings ************************************* The generic package :file:`GNATCOLL.Strings_Impl` (and its default instantiation in :file:`GNATCOLL.Strings`) provides a high-performance strings implementation. It comes in addition to Ada's own `String` and `Unbounded_String` types, although it attempts to find a middle ground in between (flexibility vs performance). GNATCOLL.Strings therefore provides strings (named `XString`, as in extended-strings) that can grow as needed (up to `Natural'Last`, like standard strings), yet are faster than unbounded strings. They also come with an extended API, which includes all primitive operations from unbounded strings, in addition to some subprograms inspired from GNATCOLL.Utils and the python and C++ programming languages. Small string optimization ========================= GNATCOLL.Strings uses a number of tricks to improve on the efficiency. The most important one is to limit the number of memory allocations. For this, we use a trick similar to what all C++ implementations do nowadays, namely the small string optimization. The idea is that when a string is short, we can avoid all memory allocations altogether, while still keeping the string type itself small. We therefore use an Unchecked_Union, where a string can be viewed in two ways:: Small string [f][s][ characters of the string 23 bytes ] f = 1 bit for a flag, set to 0 for a small string s = 7 bits for the size of the string (i.e. number of significant characters in the array) Big string [f][c ][size ][data ][first ][pad ] f = 1 bit for a flag, set to 1 for a big string c = 31 bits for half the capacity. This is the size of the buffer pointed to by data, and which contains the actual characters of the string. size = 32 bits for the size of the string, i.e. the number of significant characters in the buffer. data = a pointer (32 or 64 bits depending on architecture) first = 32 bits, see the handling of substrings below pad = 32 bits on a 64 bits system, 0 otherwise. This is because of alignment issues. So in the same amount of memory (24 bytes), we can either store a small string of 23 characters or less with no memory allocations, or a big string that requires allocation. In a typical application, most strings are smaller than 23 bytes, so we are saving very significant time here. This representation has to work on both 32 bits systems and 64 bits systems, so we have careful representation clauses to take this into account. It also needs to work on both big-endian and little-endian systems. Thanks to Ada's representation clauses, this one in fact relatively easy to achieve (well, okay, after trying a few different approaches to emulate what's done in C++, and that did not work elegantly). In fact, emulating via bit-shift operations ended up with code that was less efficient than letting the compiler do it automatically because of our representation clauses. Character types =============== Applications should be able to handle the whole set of Unicode characters. In Ada, these are represented as the Wide_Character type, rather than Character, and stored on 2 bytes rather than 1. Of course, for a lot of applications it would be wasting memory to always store 2 bytes per character, so we want to give flexibility to users here. So the package GNATCOLL.Strings_Impl is a generic. It has several formal parameters, among which: * Character_Type is the type used to represent each character. Typically, it will be Character, Wide_Character, or even possibly Wide_Wide_Character. It could really be any scalar type, so for instance we could use this package to represent DNA with its 4-valued nucleobases. * Character_String is an array of these characters, as would be represented in Ada. It will typically be a String or a Wide_String. This type is used to make this package work with the rest of the Ada world. Note about Unicode: we could also always use a Character, and use UTF-8 encoding internally. But this makes all operations (from taking the length to moving the next character) slower, and more fragile. We must make sure not to cut a string in the middle of a multi-byte sequence. Instead, we manipulate a string of code points (in terms of Unicode). A similar choice is made in Ada (String vs Wide_String), Python and C++. Configuring the size of small strings ===================================== The above is what is done for most C++ implementations nowadays. The maximum 23 characters we mentioned for a small string depends in fact on several criteria, which impact the actual maximum size of a small string: * on 32 bits system, the size of the big string is 16 bytes, so the maximum size of a small string is 15 bytes. * on 64 bits system, the size of the big string is 24 bytes, so the maximum size of a small string is 23 bytes. * If using a Character as the character type, the above are the actual number of characters in the string. But if you are using a Wide_Character, this is double the maximum length of the string, so a small string is either 7 characters or 11 characters long. This is often a reasonable number, and given that applications mostly use small strings, we are already saving a lot of allocations. However, in some cases we know that the typical length of strings in a particular context is different. For instance, GNATCOLL.Traces builds messages to output in the log file. Such messages will typically be at most 100 characters, although they can of course be much larger sometimes. We have added one more formal parameter to GNATCOLL.Strings_Impl to control the maximum size of small strings. If for instance we decide that a "small" string is anywhere from 1 to 100 characters long (i.e. we do not want to allocate memory for those strings), it can be done via this parameter. Of course, in such cases the size of the string itself becomes much larger. In this example it would be 101 bytes long, rather than the 24 bytes. Although we are saving on memory allocations, we are also spending more time copying data when the string is passed around, so you'll need to measure the performance here. The maximum size for the small string is 127 bytes however, because this size and the 1-bit flag need to fit in 1 bytes in the representation clauses we showed above. We tried to make this more configurable, but this makes things significantly more complex between little-endian and big-endian systems, and having large "small" strings would not make much sense in terms of performance anyway. Typical C++ implementations do not make this small size configurable. Task safety =========== Just like unbounded strings, the strings in this package are not thread safe. This means that you cannot access the same string (read or write) from two different threads without somehow protecting the access via a protected type, locks,... In practice, sharing strings would rarely be done, so if the package itself was doing its own locking we would end up with very bad performance in all cases, for a few cases where it might prove useful. As we'll discuss below, it is possible to use two different strings that actually share the same internal buffer, from two different threads. Since this is an implementation detail, this package takes care of guaranteeing the integrity of the shared data in such a case. Copy on write ============= There is one more formal parameter, to configure whether this package should use copy-on-write or not. When copy on write is enabled, you can have multiple strings that internally share the same buffer of characters. This means that assigning a string to another one becomes a reasonably fast operation (copy a pointer and increment a refcount). Whenever the string is modified, a copy of the buffer is done so that other copies of the same string are not impacted. But in fact, there is one drawback with this scheme: we need reference counting to know when we can free the shared data, or when we need to make a copy of it. This reference counting must be thread safe, since users might be using two different strings from two different threads, but they share data internally. Thus the reference counting is done via atomic operations, which have some impact on performance. Since multiple threads try to access the same memory addresses, this is also a source of contention in multi-threaded applications. For this reason, the current C++ standard prevents the use of copy-on-write for strings. In our case, we chose to make this configurable in the generic, so that users can decide whether to pay the cost of the atomic operations, but save on the number of memory allocations and copy of the characters. Sometimes it is better to share the data, sometimes to systematically copy it. Again, actual measurements of the performance are needed for your specific application. Growth strategy =============== When the current size of the string becomes bigger than the available allocated memory (for instance because you are appending characters), this package needs to reallocate memory. There are plenty of strategies here, from allocating only the exact amount of memory needed (which saves on memory usage, but is very bad in terms of performance), to doubling the current size of the string until we have enough space, as currently done in the GNAT unbounded strings implementation. The latter approach would therefore allocate space for two characters, then for 4, then 8 and so on. This package has a slightly different strategy. Remember that we only start allocating memory past the size of small strings, so we will for instance first allocate 24 bytes. When more memory is needed, we multiply this size by 1.5, which some researchers have found to be a good comprise between waste of memory and number of allocations. For very large strings, we always allocate multiples of the memory page size (4096 bytes), since this is what the system will make available anyway. So we will basically allocate the following: 24, 36, 54, 82, 122,... An additional constraint is that we only ever allocate even number of bytes. This is called the capacity of the string. In the layout of the big string, as shown above, we store half that capacity, which saves one bit that we use for the flag. Substrings ========== One other optimization performed by this package (which is not done for unbounded strings or various C++ implementations) is to optimize substrings when also using copy-on-write. We simply store the index of the first character of the string within the shared buffer, instead of always starting at the first. From the user's point of view, this is an implementation detail. Strings are always indexed from 1, and internally we convert to an actual position in the buffer. This means that if we need to reallocate the buffer, for instance when the string is modified, we transparently change the index of the first character, but the indexes the user was using are still valid. This results in very significant savings, as shown below in the timings for Trim for instance. Also, we can do an operation like splitting a string very efficiently. For instance, the following code doesn't allocate any memory, beside setting the initial value of the string. It parses a file containing some "key=value" lines, with optional spaces, and possibly empty lines:: declare S, Key, Value : XString; L : XString_Array (1 .. 2); Last : Natural; begin S.Set ("......."); -- Get each line for Line in S.Split (ASCII.LF) loop -- Split into at most two substrings Line.Split ('=', Into => L, Last => Last); if Last = 2 then Key := L (1); Key.Trim; -- Removing leading and trailing spaces Value := L (2); Value.Trim; end if; end loop; end; API === This package provides a very extensive set of API that apply to `XString`, please check the spec in :file:`gnatcoll-strings_impl.ads` for a fully documented list. gnatcoll-core-21.0.0/docs/Makefile0000644000175000017500000001103513661715457016574 0ustar nicolasnicolas# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GNATColl.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GNATColl.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/GNATColl" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GNATColl" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." make -C $(BUILDDIR)/latex LATEXOPTS="-interaction=errorstopmode" all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." gnatcoll-core-21.0.0/docs/memory.rst0000644000175000017500000001232313661715457017177 0ustar nicolasnicolas*********************************** **Memory**: Monitoring memory usage *********************************** .. highlight:: ada The GNAT compiler allocates and deallocates all memory either through type-specific debug pools that you have defined yourself, or defaults to the standard malloc and free system calls. However, it calls those through an Ada proxy, in the package `System.Memory` that you can also replace in your own application if need be. Like this:: procedure Ada `gnatcoll` provides such a possible replacement. Its implementation is also based on `malloc` and `free`, but if you so chose you can activate extra monitoring capabilities to help you find out which parts of your program is allocating the most memory, or where memory is allocated at any moment in the life of your application. This package is called `GNATCOLL.Memory`. To use it requires a bit of preparation in your application: * You need to create your own version of :file:`s-memory.adb` with the template below, and put it somewhere in your source path. This file should contain the following bit of code:: with GNATCOLL.Memory; package body System.Memory is package M renames GNATCOLL.Memory; function Alloc (Size : size_t) return System.Address is begin return M.Alloc (M.size_t (Size)); end Alloc; procedure Free (Ptr : System.Address) renames M.Free; function Realloc (Ptr : System.Address; Size : size_t) return System.Address is begin return M.Realloc (Ptr, M.size_t (Size)); end Realloc; end; * You then need to compile your application with the extra switch `-a` passed to `gnatmake` or `gprbuild`, so that this file is appropriately compiled and linked with your application * If you only do this, the monitor is disabled by default. This basically has zero overhead for your application (apart from the initial small allocation of some internal data). When you call the procedure `GNATCOLL.Memory.Configure` to activate the monitor, each memory allocation or deallocation will result in extra overhead that will slow down your application a bit. But at that point you can then get access to the information stored in the monitor We actually recommend that the activation of the monitor be based on an environment variable or command line switch of your application, so that you can decide at any time to rerun your application with the monitor activated, rather than have to go through an extra recompilation. All allocations and deallocations are monitor automatically when this module is activated. However, you can also manually call `GNATCOLL.Memory.Mark_Traceback` to add a dummy entry in the internal tables that matches the current stack trace. This is helpful for instance if you want to monitor the calls to a specific subprogram, and know both the number of calls, and which callers executed it how many times. This can help find hotspots in your application to optimize the code. The information that is available through the monitor is the list of all chunks of memory that were allocated in Ada (this does not include allocations done in other languages like C). These chunks are grouped based on the stack trace at the time of their invocation, and this package knows how many times each stack trace executed each allocation. As a result, you can call the function `GNATCOLL.Memory.Dump` to dump on the standard output various types of data, sorted. To limit the output to a somewhat usable format, `Dump` asks you to specify how many blocks it should output. *Debugging dangling pointer* Using a dangling pointer can lead (and usually it does) to no crash or no side effects. Frequently, freed buffers still contains valid data and are still part of pages owned by your process. Probably, this occurs more often on linux compare to windows. Writing 0 or 0xDD pattern when a memory is freed will be (because of the exception that will be thrown) detected at the first usage of a freed buffer. The crash occurrence will be higher and less random. This makes solid reproducer more easy to build. For dangling pointer usage debugging, use Memory_Free_Pattern parameter when calling `GNATCOLL.Memory.Configure` procedure. *Memory usage* Blocks are sorted based on the amount of memory they have allocated and is still allocated. This helps you find which part of your application is currently using the most memory. *Allocations count* Blocks are sorted based on the number of allocation that are still allocated. This helps you find which part of your application has done the most number of allocations (since malloc is a rather slow system call, it is in general a good idea to try and reduce the number of allocations in an application). *Total number of allocations* This is similar to the above, but includes all allocations ever done in this block, even if memory has been deallocated since then. *Marked blocks* These are the blocks that were created through your calls to `GNATCOLL.Memory.Mark_Traceback`. They are sorted by the number of allocation for that stacktrace, and also shows you the total number of such allocations in marked blocks. This is useful to monitor and analyze calls to specific places in your code gnatcoll-core-21.0.0/docs/mmap.rst0000644000175000017500000001402713661715457016624 0ustar nicolasnicolas.. _Reading_and_Writing_Files: *********************************** **Mmap**: Reading and Writing Files *********************************** .. index:: mmap .. highlight:: ada Most applications need to efficiently read files from the disk. Some also need in addition to modify them and write them back. The Ada run-time profiles several high-level functions to do so, most notably in the :file:`Ada.Text_IO` package. However, these subprograms require a lot of additional housekeeping in the run-time, and therefore tend to be slow. GNAT provides a number of low-level functions in its :file:`GNAT.OS_Lib` package. These are direct import of the usual C system calls `read()`, `write()` and `open()`. These are much faster, and suitable for most applications. However, if you happen to manipulate big files (several megabytes and much more), these functions are still slow. The reason is that to use `read` you basically need a few other system calls: allocate some memory to temporarily store the contents of the file, then read the whole contents of the file (even if you are only going to read a small part of it, although presumably you would use `lseek` in such a case). On most Unix systems, there exists an additional system call `mmap()` which basically replaces `open`, and makes the contents of the file immediately accessible, in the order of a few micro-seconds. You do not need to allocate memory specifically for that purpose. When you access part of the file, the actual contents is temporarily mapped in memory by the system. To modify the file, you just modify the contents of the memory, and do not worry about writing the file back to the disk. When your application does not need to read the whole contents of the file, the speed up can be several orders of magnitude faster than `read()`. Even when you need to read the whole contents, using `mmap()` is still two or three times faster, which is especially interesting on big files. GNATColl's `GNATCOLL.Mmap` package provides a high-level abstraction on top of the `mmap` system call. As for most other packages in GNATColl, it also nicely handles the case where your system does not actually support `mmap`, and will in that case fallback on using `read` and `write` transparently. In such a case, your application will perform a little slower, but you do not have to modify your code to adapt it to the new system. Due to the low-level C API that is needed underneath, the various subprograms in this package do not directly manipulate Ada strings with valid bounds. Instead, a new type `Str_Access` was defined. It does not contain the bounds of the string, and therefore you cannot use the usual `'First` and `'Last` attributes on that string. But there are other subprograms that provide those values. Here is how to read a whole file at once. This is what your code will use in most cases, unless you expect to read files bigger than `Integer'Last` bytes long. In such cases you need to read chunks of the file separately. The `mmap` system call is such that its performance does not depend on the size of the file your are mapping. Of course, this could be a problem if `GNATCOLL.Mmap` falls back on calling `read`, since in that case it needs to allocate as much memory as your file. Therefore in some cases you will also want to only read chunks of the file at once:: declare File : Mapped_File; Reg : Mapped_Region; Str : Long.Str_Access; begin File := Open_Read ("/tmp/file_on_disk"); Reg := Read (File); *-- map the whole file* Close (File); Str := Long.Data (File); for S in 1 .. Long.Last (File) loop Put (Str (S)); end loop; Free (Reg); end; The above example works for files larger than 2Gb, on 64 bits system (up to a petabyte in fact), on systems that support the `mmap` system call. To read only a chunk of the file, your code would look like the following. At the low-level, the system call will always read chunks multiple of a size called the page_size. Although `GNATCOLL.Mmap` takes care of rounding the numbers appropriately, it is recommended that you pass parameters that are multiples of that size. That optimizes the number of system calls you will need to do, and therefore speeds up your application somewhat:: declare File : Mapped_File; Reg : Mapped_Region; Str : Str_Access; Offs : Long_Integer := 0; Page : constant Integer := Get_Page_Size; begin File := Open_Read ("/tmp/file_on_disk"); while Offs < Length (File) loop Read (File, Reg, Offs, Length => Long_Integer (Page) * 4); Str := Data (File); *-- Print characters for this chunk:* for S in Integer (Offs - Offset (File)) + 1 .. Last (File) loop Put (Str (S)); end loop; Offs := Offs + Long_Integer (Last (File)); end loop; Free (Reg); Close (File); end; There are a number of subtle details in the code above. Since the system call only manipulates chunk of the file on boundaries multiple of the code size, there is no guarantee that the part of the file we actually read really starts exactly at `Offs`. If could in fact start before, for rounding issues. Therefore when we loop over the contents of the buffer, we make sure to actually start at the `Offs`-th character in the file. In the particular case of this code, we make sure we only manipulate multiples of the page_size, so we could in fact replace the loop with the simpler:: for S in 1 .. Last (File) loop If you intend to modify the contents of the file, not that `GNATCOLL.Mmap` currently gives you no way to change the size of the file. The only difference compared to the code used for reading the file is the call to open the file, which should be:: File := Open_Write ("/tmp/file_on_disk"); Modifications to Str are automatically reflected in the file. However, there is no guarantee this saving is done immediately. It could be done only when you call `Close`. This is in particular always the case when your system does not support `mmap` and `GNATCOLL.Mmap` had to fallback on calls to `read`. gnatcoll-core-21.0.0/docs/note.png0000644000175000017500000000430713661715457016613 0ustar nicolasnicolas‰PNG  IHDR"":G ÂbKGDÿÿÿ ½§“ pHYsHHFÉk>gIDATXÃí—kl[gÇçûØq|KâØ¹‘‹suœÛæ¦]Òµ ÓÚµ5ë†&b€´O“úBB|BÀ$$@ã ©hRÕ‚h«Q.em¤)¬£i:'nI¶Ù^Ü&qîqm×ñíø>¤ mÚn)Ò>ðH¯^éÕûüßÿÿÿ>çyáÿñ éÖ>< ”Y hŸÝ×úûû¿þÜsÏ“$i>Dý~ÿ‰Db4®®®~$õÿ5{cccïÑ£G£ÑXuäÈ‘ªõõu_0üÒõë×3¡PèÆÚÚÚÕp8|ejjê=`˜òŸžž@Eeee«ÕjEUU¬V+V«U¨¨¨äl6ÛyóæÍÎH$òÒüüüJ8} ¹\n,™LNq@á!r~,¿ßOKK‹¯££Ã P(H&“ȲLQQ‚ `4ikkÃëõŠŠ¢8ãñ¸3‘Hô-..f'&&~¿q~~~<‹] …B×’ÉdX¸;°Ö„®®®_?~üåÚÚZ2™ ‹‹‹¨ªºq«ÕŠÅbA¯×#¢("[«ªŠªªÌÏÏk“““LNN¦^}õÕãKKKÇÕ#öæææŽ²²24MC§ÓQ[[K¡P “ÉËåH$¬­­QWW‡Ñh@Ó´-P’$Q__/466âõzÍ'NœÐ/--=š4¢(¶ïÚµ«Ê`0 ±XŒL&ƒ^¯Çd2a·Û)//GÓ4$i£‚°ÅÈæ÷&;@ “ËåÞÞº# ’$¡×ë½­­­N£Ñˆªª”––’J¥Èf³$“IVVVÐ4 ‹Å‚ÝnÇd2¡Óéî° L&&&ÖgffÆúúúvÎH¡PÐ÷÷÷wº\.£(Šc6›QUEQP…|>ÏíÛ·™››Ãf³Q]]}ˆ;̋ŘÌçó‹£££$³½½½Ãår°´´ÄÈÈv»ŸÏGYY²,£iÚ€íÒlÎ’$‰DXZZnoš}ËC`2™*«««Û].š¦qúôY. ó׳çøÁwŽqöÌâñ8z½~ë¦lÎÛ‡(ŠLOO«@à]ŸÏ—۞¤R)¼^¯·¡¡Á!I…Bêê*‚S!ú[J˜[Ôøû…1®ú¯sàézzz°ÙlètºûØL&ÃÜÜÜh4ŽF£ìˆ úÁÁÁ=GÈd2TT8)â5U=Tv|ºT ã㣼ö›“xÚ.ñôÓOáp8°ÙlX­VdYF–eDQ$355b£üß”Æl6˜Çn·ÛªªX,:»zhöîCäLâ%ÚZJس§ö®ÃÌD³œþãVWWÉçóD"Âá0Š¢luttôýgžyfñA9ÈÈÑÔÝÝ]¥×ëQU•ÙÙYú¿ÉðŸc8g~IEQYYIr½@dAA,Å"UDÐ)+µQZZBQ‘ñNáÚ¨ åååìò=Æz*Áo¼( ìÞÝM»·Ë”ͱ{aaÖx;7[ìé\.ó‘)Ù»wïw_|ñÅ£Afz!ÏZReaMAEbÉW¦Òh¤³J ²ˆx×/Ý`0àñ´ÑÚÒÈùóá›oj==íìß7`DSßÍ›7l+ËÑ+l¼îrøða¢ÑhëÐÐз8P¤FYÄd”0›Dry°[$”‚ÆZ²ÀªÂbLAŒ²€Nº·¬ÛívææfoÿèÇ?üí{SS!³Y®{òɽEfK©72‰[ÿÜ”é³:tˆñññÊt:­¸Ýn,f3Ó^UÕ(³¡ªW4ÒY•èŠÂJ\¡Ê¡G+([[(âôéÓs™túWo½5»üìÀÀÀ—}¾=ûË¥/ÍÌL¿L¶žµ¥¥…7n”655}Áív÷»\®ÝõõõM>ŸÏèñxd»Ý.˜L&ôz=‹……‚Á ñxœ‹/*'OžüùÂÂÂ÷ï2h¹ÉdÚ¯Ó銉ęMy>ªy6æÒÒÒš’’’=%%%Otvv¶õööV7448«ªª 555Øív6›¦x<ÎÄÄÁ`+W®d.\¸p2¯¸¸x)•Jmß_à®§ÅNºø»Ãx‡§···³¹¹¹»¢¢¢£¶¶Öb6›‰F£- FÀï–——_cÛ³áañ¨@¶3æeÙYSSó¸N§«SUµH$Â+++WUU ¹O°ÿâ'<Øg#þ Þ.n¢fRÌIEND®B`‚gnatcoll-core-21.0.0/docs/tip.png0000644000175000017500000000413713661715457016443 0ustar nicolasnicolas‰PNG  IHDR"":G ÂbKGDÿÿÿ ½§“ pHYsHHFÉk>ÿIDATXÃÍ—klÕÇçÎìÃk¯ml‹Ä8N(!)!@bL¡P PC)êKU•Z µ¤V Vê‡B%*P¥RT)¨‘ª€ªªR*¨ú—ÔðHÄu¤«¹«»ç7ÿ{ÎÿÜ…ÿ“KÎõ€ŽlDA$Y¢ ¸ä®61¸4Jæj“¹$adÓÂY㘳C\‘DÁ%Y'&]o¯anRZ©C»}·ðùI jD@A5 ¤ÔI¢@ .Òd);ÔNQ§¨Ö£É@å³)¢#—7*QSÁñÄÉ€É"’Iæ’IæøÉs⟢”ÔÞJ‡òçV$ÐÚWA$Ù %¹ƒAœAmMÅEŠ .r¸Ø¢Ö‚u¨ZÔ)‚Ò M£2þrˆ&HYÊñQñió=mõ¼vÏä>8´/—­ŸÏ Qºþ&Ü|M¦ÒÓ›‰wçbTâôŨ«oöÙё˨§9*">‘,=9ïåfÚÿøôø€Ÿ-Ü ˜/ œß^ô½ ¨Î¡ñH”÷\¾Îí}øwm£­³QLGàBTcDm QuU¤¢aKƒŠHãeiÉdúíѵïáîë®îÿÖM_éë¹|Ãy[MR¦¶Ì'£Ó¼þæx´÷É¡OFg·?ñ«ü+¥D9®‚F 0Í¥8PmÙØ ’$Û¡&ƒñr´ø¹{¨·«‡—¾`ê¼5Q£‡×B\ièVP'à xüçÍÒÀ-ׯì+¶eÒôIS¨)¨4 šækWû™ ûü+·mº(’*N“"¨bPƤ§ Tæ½èüî\‡ñÓçbÔ…h 6HµÖè$ÍAM•U¥·[X¿Úô½ý¡ë¤5UBROk¸ûh|kÀ ”¹øÂÖL[¤:H«#ÆFŽPèl£«·lµUJ“³ä³2~\WØ÷ÕP»§fµ•™'ФDu¹Å'9L´j«h¼tŸ®²¦×yÿÐÌTX.A\;O6[&›pa™Œ136A¾Å‹´µ;°åTÁ 'NVùè¿öã­[dŠ9E“ÜhF®˜„–ŽÆ“U ;&ªúýÛ2¾¶orxzrìDóˆgåê +VgY³¡•b‡cÕº<½útv[ˆ°‹Ø°Ì»ÃálPfï¦[X¤Údh§(RwDWwFÔ:ÔZÂØn½Ý›ŽÃÊÎç^<¾¨ñ<Ø9ˆæð(‘ñ«œ×³îŠ-àûÄ•pl¬Ì¿_sï}÷VÞâ¤&Ç¥>£g!UÃ*êX‹ÚX².ÞºE^}áÕ©—^ë¤âæ ž%*A<‹¸Ú_ÉŒ^G”=>¾¦ýõ}Ý—PâB˜,? .…n‘A‹î7MX€C‰¡ŠYýU3WúÙô±K6l oÕ*å2ž±l¾,C¦v>3Þc‚°i;ÎX-§IÂ׎‹º"Húƒ‚SÏÓxtfzª488ØV(ð<8Ž9ztçâ±uýœ ZW¢ÒѨÆiAä*E÷+Ð Œ/ÁÔ®\{k|hÏžÝ3ímëׯ§³³“0 : Ã‡Ž<÷“Ì`O—œ§ƒX–¬MÂì_ö\r„ôñžyžÂýgSi¹;›Íæ}ßÇ©RY,è??¸ïàC ¡§øÆç9#ŒbèCný&+÷¼Ë•;º<ý=<óÁ¿Ø×éã&Ï8+Ä9AÎ# True); for F in Files'Range loop Put_Line ("File is: " & Files (F).Display_Full_Name); end loop; Tree.Unload; end Test_Project; Defining a project with user-defined packages and reading them ============================================================== .. highlight:: ada If you want to use `GNATCOLL.Projects` with a GPR file that contains specific packages and attributes, you must procede in several steps. The following example will show you how to do it: .. code-block:: ada with Ada.Text_IO; use Ada.Text_IO; with GNAT.Strings; with GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; procedure Test_Project is Tree : Project_Tree; Project_File : constant Virtual_File := GNATCOLL.VFS.Create (+"path_to_project.gpr"); begin -- 1 if Register_New_Attribute ("String", "Package_Name") /= "" or else Register_New_Attribute ("List", "Package_Name", Is_List => True) /= "" or else Register_New_Attribute ("Indexed", "Package_Name", Indexed => True) /= "" then raise Program_Error; end if; -- 2 Tree.Load (Root_Project_Path => Project_File, Packages_To_Check => All_Packs); declare Root : constant Project_Type := Tree.Root_Project; -- 3 String_Attribute : constant Attribute_Pkg_String := Build ("Package_Name", "String"); String_Value : constant String := Root.Attribute_Value (String_Attribute); Indexed_Attribute : constant Attribute_Pkg_String := Build ("Package_Name", "Indexed"); Indexed_Value : constant String := Root.Attribute_Value (Indexed_Attribute, Index => "Index"); List_Attribute : constant Attribute_Pkg_List := Build ("Package_Name", "List"); List_Value : constant GNAT.Strings.String_List_Access := Root.Attribute_Value (List_Attribute); begin -- 4 Put_Line ("Package_Name.String: " & String_Value); Put_Line ("Package_Name.Indexed (""Index""): " & Indexed_Value); Put_Line ("Package_Name.List:"); for Val of List_Value.all loop Put_Line (" Value: " & Val.all); end loop; end; end Test_Project; And the corresponding project file: .. code-block:: ada project Path_To_Project is package Package_Name is for String use "some string" for Indexed ("Index") use "other string"; for List use ("first item", "second item"); end Package_Name; end Path_To_Project; Step 1: We register all the attributes that we want for a given package. If the package does not already exists it is created. Step 2: We load the project into the projects hierarchy. We tell ``Tree.Load`` to check all packages otherwise it will not load any packages. Step 3: We read attributes from the project. An attribute can be an ``Attribute_Pkg_String`` (representing a plain string) or an ``Attribute_Pkg_List`` (representing a list of strings). Step 4: We can do something with those values. Here we print the plain string and the content of the list. This program should output: .. code-block:: text Package_Name.String: some string Package_Name.Indexed ("Index"): hello world Package_Name.List: Value: hello Value: world gnatcoll-core-21.0.0/docs/vfs.rst0000644000175000017500000002570713661715457016477 0ustar nicolasnicolas*************************** **VFS**: Manipulating Files *************************** .. highlight:: ada Ada was meant from the beginning to be a very portable language, across architectures. As a result, most of the code you write on one machine has good chances of working as is on other machines. There remains, however, some areas that are somewhat system specific. The Ada run-time, the GNAT specific run-time and GNATColl all try to abstract some of those operations to help you make your code more portable. One of these areas is related to the way files are represented and manipulated. Reading or writing to a file is system independent, and taken care of by the standard run-time. Other differences between systems include the way file names are represented (can a given file be accessed through various casing or not, are directories separated with a backslash or a forward slash, or some other mean, and a few others). The GNAT run-time does a good job at providing subprograms that work on most types of filesystems, but the relevant subprograms are split between several packages and not always easy to locate. GNATColl groups all these functions into a single convenient tagged type hierarchy. In addition, it provides the framework for transparently manipulating files on other machines. Another difference is specific to the application code: sometimes, a subprogram needs to manipulate the base name (no directory information) of a file, whereas sometimes the full file name is needed. It is somewhat hard to document this in the API, and certainly fills the code with lots of conversion from full name to base name, and sometimes reverse (which, of course, might be an expansive computation). To make this easier, GNATColl provides a type that encapsulates the notion of a file, and removes the need for the application to indicate whether it needs a full name, a base name, or any other part of the file name. Filesystems abstraction ======================= There exists lots of different filesystems on all machines. These include such things as FAT, VFAT, NTFS, ext2, VMS,.... However, all these can be grouped into three families of filesystems: * windows-based filesystems On such filesystems, the full name of a file is split into three parts: the name of the drive (c:, d:,...), the directories which are separated by a backslash, and the base name. Such filesystems are sometimes inaccurately said to be case insensitive: by that, one means that the same file can be accessed through various casing. However, a user is generally expecting a specific casing when a file name is displayed, and the application should strive to preserve that casing (as opposed to, for instance, systematically convert the file name to lower cases). A special case of a windows-based filesystems is that emulated by the cygwin development environment. In this case, the filesystem is seen as if it was unix-based (see below), with one special quirk to indicate the drive letter (the file name starts with "/cygwin/c/"). * unix-based filesystems On such filesystems, directories are separated by forward slashed. File names are case sensitive, that is a directory can contain both "foo" and "Foo", which is not possible on windows-based filesystems. * vms filesystem This filesystem represents path differently than the other two, using brackets to indicate parent directories A given machine can actually have several file systems in parallel, when a remote disk is mounted through NFS or samba for instance. There is generally no easy way to guess that information automatically, and it generally does not matter since the system will convert from the native file system to that of the remote host transparently (for instance, if you mount a windows disk on a unix machine, you access its files through forward slash- separated directory names). GNATColl abstracts the differences between these filesystems through a set of tagged types in the `GNATCOLL.Filesystem` package and its children. Such a type has primitive operations to manipulate the names of files (retrieving the base name from a full name for instance), to check various attributes of the file (is this a directory, a symbolic link, is the file readable or writable), or to manipulate the file itself (copying, deleting, reading and writing). It provides similar operations for directories (creating or deleting paths, reading the list of files in a directory,...). It also provides information on the system itself (the list of available drives on a windows machine for instance). The root type `Filesystem_Record` is abstract, and is specialized in various child types. A convenient factory is provided to return the filesystem appropriate for the local machine (`Get_Local_Filesystem`), but you might chose to create your own factory in your application if you have specialized needs (:ref:`Remote_filesystems`). file names encoding ------------------- One delicate part when dealing with filesystems is handling files whose name cannot be described in ASCII. This includes names in asian languages for instance, or names with accented letters. There is unfortunately no way, in general, to know what the encoding is for a filesystem. In fact, there might not even be such an encoding (on linux, for instance, one can happily create a file with a chinese name and another one with a french name in the same directory). As a result, GNATColl always treats file names as a series of bytes, and does not try to assume any specific encoding for them. This works fine as long as you are interfacing the system (since the same series of bytes that was returned by it is also used to access the file later on). However, this becomes a problem when the time comes to display the name for the user (for instance in a graphical interface). At that point, you need to convert the file name to a specific encoding, generally UTF-8 but not necessarily (it could be ISO-8859-1 in some cases for instance). Since GNATColl cannot guess whether the file names have a specific encoding on the file system, or what encoding you might wish in the end, it lets you take care of the conversion. To do so, you can use either of the two subprograms `Locale_To_Display` and `Set_Locale_To_Display_Encoder` .. _Remote_filesystems: Remote filesystems ================== Once the abstract for filesystems exists, it is tempting to use it to access files on remote machines. There are of course lots of differences with filesystems on the local machine: their names are manipulated similarly (although you need to somehow indicate on which host they are to be found), but any operation of the file itself needs to be done on the remote host itself, as it can't be done through calls to the system's standard C library. Note that when we speak of disks on a remote machine, we indicate disks that are not accessible locally, for instance through NFS mounts or samba. In such cases, the files are accessed transparently as if they were local, and all this is taken care of by the system itself, no special layer is needed at the application level. GNATColl provides an extensive framework for manipulating such remote files. It knows what commands need to be run on the remote host to perform the operations ("cp" or "copy", "stat" or "dir /a-d",...) and will happily perform these operations when you try to manipulate such files. There are however two operations that your own application needs to take care of to take full advantage of remote files. Filesystem factory ------------------ GNATColl cannot know in advance what filesystem is running on the remote host, so it does not try to guess it. As a result, your application should have a factory that creates the proper instance of a `Filesystem_Record` depending on the host. Something like:: type Filesystem_Type is (Windows, Unix); function Filesystem_Factory (Typ : Filesystem_Type; Host : String) return Filesystem_Access is FS : Filesystem_Access; begin if Host = "" then case Typ is when Unix => FS := new Unix_Filesystem_Record; when Windows => FS := new Windows_Filesystem_Record; end case; else case Typ is when Unix => FS := new Remote_Unix_Filesystem_Record; Setup (Remote_Unix_Filesystem_Record (FS.all), Host => Host, Transport => ...); *-- see below* when Windows => FS := new Remote_Windows_Filesystem_Record; Setup (Remote_Windows_Filesystem_Record (FS.all), Host => Host, Transport => ...); end case; end if; Set_Locale_To_Display_Encoder (FS.all, Encode_To_UTF8'Access); return FS; end Filesystem_Factory; Transport layer --------------- There exists lots of protocols to communicate with a remote machine, so as to be able to perform operations on it. These include protocols such as `rsh`, `ssh` or `telnet`. In most of these cases, a user name and password is needed (and will likely be asked to the user). Furthermore, you might not want to use the same protocol to connect to different machines. GNATColl does not try to second guess your intention here. It performs all its remote operations through a tagged type defined in `GNATCOLL.Filesystem.Transport`. This type is abstract, and must be overridden in your application. For instance, GPS has a full support for choosing which protocol to use on which host, what kind of filesystem is running on that host, to recognize password queries from the transport protocol,.... All these can be encapsulated in the transport protocol. Once you have created one or more children of `Filesystem_Transport_Record`, you associate them with your instance of the filesystem through a call to the `Setup` primitive operation of the filesystem. See the factory example above. Virtual files ============= As we have seen, the filesystem type abstracts all the operations for manipulating files and their names. There is however another aspect when dealing with file names in an application: it is often unclear whether a full name (with directories) is expected, or whether the base name itself is sufficient. There are also some aspects about a file that can be cached to improve the efficiency. For these reasons, GNATColl provides a new type `GNATCOLL.VFS.Virtual_File` which abstracts the notion of file. It provides lots of primitive operations to manipulate such files (which are of course implemented based on the filesystem abstract, so support files on remote hosts among other advantages), and encapsulate the base name and the full name of a file so that your API becomes clearer (you are not expecting just any string, but really a file). This type is reference counted: it takes care of memory management on its own, and will free its internal data (file name and cached data) automatically when the file is no longer needed. This has of course a slight efficiency cost, due to controlled types, but we have found in the context of GPS that the added flexibility was well worth it. gnatcoll-core-21.0.0/docs/adacore_transparent.png0000644000175000017500000001511013661715457021657 0ustar nicolasnicolas‰PNG  IHDRª3“ tEXtSoftwareAdobe ImageReadyqÉe<êIDATxÚì]xÕµ>3[$Y²dÙ–-Ù8Æ6Œ©ǘ’$ „ÄSbƒß ð¤‘Nz!¼pxÄ”4 <Ó›!‡ÀWܰd«n›yçÌü£½ºº³Ú]Éâ} ó}çÓîìÌ[þ{Îν3ŠÒÜÅôϱþ޵ ß+X?Ïú‡»CÆ%*‹ÑwN›E%1Ê8. ÉàI´ˆk*YG²:Ê1‹õÖ®w©%¬qÔK=öÿM¤ŸªX„îÇZƒ‰ÅDkC_®…ndmjáò{Ö³XÛ•cå¬?býÖÐÜ7ÊÁ¬ X?ÂZ pZùØq€v'ëÖÞ¯hxþ4Ö+à 4p¿Ã‡0ÙC&³þ‘õuÖ+Ye“'HE"¬ãXgä4ÔüäÜ.uëg†°ÙMîb}ƒunŽ~N±6²naÝ̺uF«yzÈõç/3•Ï.:4¢›Çzëû¤û³þ‰õˆß·²ÞÇú ëJÖ] ··à¡êXaÍúIÖѬK‡€šŸHÇ |_Ãz¯ÆKå÷²þó}ÚŸ2‘ÿî]—¿²ÞÆúkKŽ2‚`ê5Ö;пÓT ¹þ±V=()¨ÿ2”ê9˜2}6mбv‚÷®GßåC!,62†lÄXÖÃñW‚ê'‘½ÈU?9¿õ‹`\›P¿5!¼€:ÖÁ>¨|^ˆô\Íý¿÷@ç%§³^®[nø]:úyÖëYïkXˆÈêÕq¬°ƒÉ 9Wû2ë¬÷øV5BÿØÙLK¦Èf ëºÞ2êWATy AÕ`Éñ¬—€fÔ…œ“P…²-w“ ŒñX\'ãpèHë—X¿ð©êw†òdîjÖO¿ØaÇgYë§ uýŸÔbfŽ*·kßź~ªÀÎàüà;)¤Áä:v,n*ò?ŠHZ@t>,a$ÇùÃpŸ»Q¯éžûom£–Öª×uû HÒågƒÐ£žD Q—ãÜèɵäçw¿K~¾—BŒP½©¸F&ô(ü6™‹j 5ÉïUÖ‹r€4À¢ô÷ŸÑçû Ô‹ AT»vìCúäÃtø  g„üî¢óô §‘ŸŸ’Ç=$à™f(w,ÿ›ä/UfBê·”ZCI‡Z:<[¬àþº5kÿÄ €TèÆã¬gÆ1×6¸V]0ßøM5üž4Œqté ÏëVTÊ¿ÔM-û L®eðÈú¸J›$ÅùÁ|]¿E3µ¸7ÄM® uJµTäqwRïeEÐSp¡¯âÕ¬G²žÃ: ®ü0Õ¥ÜK“Ï¡“Rè„Ç•ŽjCÛJQÖ§àJÇ*×ïËúKrœ ·ín¥ý÷#>ØpŸG¤ß†eÔE¨ÊmÈÛp§5è³€¨c.}÷¨Ã6v,òu :FÛÀé«À5 Öð!ôY mðL×+ŽBÇŽµø¸2†²Áè¬'ʘõÔKµïOÀ}šä4‹( Z€™&G‘Ÿ×—eÿ ¹ÖpÍ ¬7a å8œª/Ùˆö,Ëä$1)D—€&¨}[Õ¿®nh¾ÿ„Œ×Ï0”±·—:ç@*“ø  d#,¡¸Ö»@Y™‚¶žªñBÝÊ`¿ý†õ䯬e°ÀX,Öúm&É+!}þT‚òi†R(ÆÕvAÔIÚ±‡sœÿ”XsÁõÂd¡¤2Ð'‡€T•ÑyÍy° ­Dâo!XÐe^:;„£ €Ù¼A*ô§†ã—‡€” mÞœˆVÝ¢ÎB%mü&ý¥,W1n'i€ÿêòcÖ¿02ÉîÃÚé±<Çù†ˆïð “Ì2ðßp¯|÷_¾‚Žß["Êë=)¿}ì–ÖŽáÉTIJ,=8h§ÂWž ‘kبrkH¤&Âréò-ÍC„¥ÿ¾8Å$ã ô9Î7É5”ݤ´.ÉT}'Ô -já¯]}ä`»-“SŠ ZY˜^ÙKÀpz[v«’RéÇ‘ U‰ÁjgöR]&2 mEf<‹Pµ9}\·.?LÎÔŒ›LÜ› ¬ÛzÎŽ êCziIƒ°ÖºšI=wÞyºÓ \eQ.®åC9Ü›*†\l™càÑ>R^ý‘Ù†tÐÝH*­ˆ)L^4—ü|3Wu~×Ó„[ †`&Òin ©SÎá& 7pÇ¥š«8ƒéj7̬§Èe êòj?]t!VSV“ÎBÇ–,¦*‰^Ó³3iËšÿ„²’úŒ“ÑûN¶î N6ÛÐòêѶ^”’ùÙ7™°Ûs”7ÒTÝ€ì Ž\è>è¨é‚c 7ÔwÿH^îÐntÜÊËønÚ¯ÙŸÁÍ÷ÑŽ‘Ú:°Ð÷4YJe¬TÚájí2\$¢ë–1>ÜÞò¤]) ¨U9€š¡Ü+€Õ†I?`|ÝÔ 3I·v-H+@ £ {S"ˆÐyù¿ÉÏ1 íØ•o`–H¥¥ å\òõ{¡þö—gêo5ŒÆ0—A€Çò˜o ì|½ ”¹Cj™!ˆzYs1rá6¸þTˆE×Ó"Âo’¿Ø´ƒgÄwº*§¢O¹æk¬¿*:ðÙíjn§é-Éù^­ýzÊ>åÅp ³Yäíö•ezü2<|‹Òkoí  ;)î§£åÑŒŸΜŒ +ûa?]`ìòDBxÿu†1¼3O° §ý³![ [ÿ ÀD’Ô–l*Ò7æÈŽ*Ù˜2¾Àò‚çØºzžá¤{(¥ògÃñ`çö!îFhÈ °Dâ¢&ÂÍ ç™ƒëRhÂ2êý PT)g—!à û`¦žùÂ*Dð’%¸AI·i¶4È'ºtï›(í8dùO¦Š¥º-$Ý%í…É#.FÛ$ ™z>7 œûX¨Õ `G+–o•Vv5â‰ëA9ª•zVã˜L"YrÕd;ã×K¯BÚÏħWÁΆW«ÃD•%âihç…0BÁ˜Es ÊWhl#:¶m*~¬³š,þ7xeR$·Sø{«œçTqƒ»‘MïiYƒË+ñY2o5K!šÞŽz|i´ ®˜_Ô®ûyÒDŠ.úðatèäñü±;ó2Vnb–ÈEŒ–T ]­L(é³O†”Õ¨x’ [Óf™3˜\¾ˆ3µ”ÞA!Ù ¡x7Sø~ä$ÆÕU KÜ%¾|Ά;ipÙm4ÃÖ¸¨¬õŸªpÈùèøÎz2 .3®åäîÀ›©wB\ÍŠUÿ²ÁÒI§L¡ì#7µ PÖ‹¨÷EÖ}Û-Û°ƒR\)oQyå‰õËgÃL•kåGݕ߂q»Ñàf#Zg@k ïóßÈÂ즽#2QO‚‘Ê„dk*1éª(ûx·.³œ€4bÀ¥â¿À Ͻ…²o®kÄlš§ÍÖ aúe#p®ÓIpØ#` ÒÐm¸W#ePåW Pî•(ñ.—Á¯Ü žÝ¦Ô=k¹#QÚ´±þµ¹ž¢%edÙQr}° YŒÁ e[Ý&ÊýÛAjp)øò× Þà pàïÀ…çJҧѶ«pÍU”;ù¾GÁ…|n2õ›´×ŽÄ¼¿Úu$èÌ ,¬ÝâÑÿ#´8pý£µ ¸ì@®½Ç0{\ë5zÇ2À”å¬y,øÛŠ+®GG/'óû*qŸà¹ÎÁàá O«$Ë{/!Ó‘özÄ-»Ã+ßå‘4Í)k£iãªi⸲ªêÈ.eåÁ³l²œ 9Nw¬'AÌ~à¤û‚/VâNí˜ ;á¶ßB;{l%´,‡Þ) [¨ÃÁñ„rŒÆ¤Ø‰¶I»Þ4MÛŽËJ™”Z®Úæ-V˜²-Çéh¢ÔÖU­C±qþâ¢+™ŒqT¢ŽÓà!†Sv»d#ºÚÙ30x7ßá0LÖŽìÒš.öÖ™Hï̓êv§ ó¿O÷4´²eö¸‡‹cø=ÂãéDzžã±Hu`ß;º™&W´SŸ+®?•f÷/£HiÅËÇÍ TÖP$^N[—A+@°Êk,®·Ë5Û9ß³\ÉN9‹Üã2“ÖÎAÃd‚èïÇr3Þ…n°³J™®¼Ÿ ™©KG3Åk&“뀥^…½ˆÓ¶›:6¿Léæm䦻<à–eü•TPdø(ŠÇ'³wqÂËêK‚>’>‰¾« 劜=º‰®l¡4÷rG:F ‰8Õ'c´5£zÇ¢(¯‹f耲.ϼÞ^æZFű{:”úCFÄviJpÄú’g‘Æ‚K;¶Jùä Æ´}`†QÚ›\ÝèáŠmÿ»]€#–É ´Æò(ùÞ‚ë÷€ÚÆ+áÝ/^.@ezÓÕìyŸ ýö ‚”kxTe;}±¶žêz‚”×#üÛùþ4ˆ·¬¹O À&@‘ëSŠfœì±´$â[G9?Pò÷Ûþ¹ÓÈé®âßÊYkpL¢Sõqœ[p|¸w_¤¥X¬¨WæGã»”½Ñ6»DØ›-Ûó^GNRž_ÅÀYÄfµV¸¬ÍçÉ_±š¢v´¤H+,S3Ñ´™­c«wŽw¾ù8ƒôqÜw²›å|¿¼XnˆR^÷ﮇÔ.-¢-´Ão_ mŠz4HΠÝÉö]ìÚ}:!m¶ý~ˆ&H«K“tJMƒžTo·ù=X®có|iRr¢êîó¤á.%°P×5º¥³°"ÕŽ”Ž h"QV³–NRTKáWV{9UIˆK¾ðý\ä-¯GÙ Q×i¹áñHý i¬Ká=®6¬®¥•쉕m»í ¢–s\Š~úÚ7‰zî³ÐOÉiº8<„‹ºÆÈ¼g"(+aÈ•*©Ãð{ØQ×UB[mBvGƒgQ¹KF ?´ÄÂoû8V™‚§#×RöŸ2•¥Å§¾¸²ûP§ãø,Qþ˜zo –޹Vì‘êz  êÀb°êbõ¾ª¹qU®Â’ ¬½Ýï=]&ÆŸ¨÷žíø;e7‰ŒF’^v!}ÖðBL’;ñ=x=Ï‘†:‡Áý^Ʋm° îBä6·biöë”ýKaTîq1Vüäü›”•Æpî³H¥=¬ÔG&ðJ”±}+nΓEŠÇЖ§‘‚œŠº~YiË/Ñw¥D4ˆ®ßòƒ‘<²³¹ê$ï¸$«FÁFï[)û…Òß X‡äûÛ˜ È+1X_E~3 @¯Â@} Ñ­÷Ñæ_(ûôÀÂ"ze$&Dðb‰]8v øøF¬"ý ¼ãl°J+ïX,–Üe¸×™XÈ)ÅÂëXwÿ2¬çlò_¤ö"úðSË"xƒË°¬^ŠþOV¸äº_ã>ãqý©˜\Wb,Ôïrä«ïÀ÷ïÐ5XÀ°1a/§ì‹ˆ¨N*Q>GÓ'V÷ûm7‹0CoRÈ“°âô :q­’Ô×-jn¿ ç.PŽÎïR–!oU¾Lq{Á4 &ÈðÑg0I&ЩÇéà’‘¿™û&e™øf d3¨… ìÏ01~ƒvOÓU*`ÉMKÑŸÀßo`~ í½„²¥,p‚DÏ£ÁË*jºð<èÎÀ…gb²ß…þ˜¥-™g°.c®ÀÓˆ?¤_åå²×BöQœ€ú<œõÊ#‰îú9ʾ4–¢…µ ~Õ?¶±FÙpÖà³ÇcV AŒn[1ˆ“°r#ß¡ì?ÇPm}\Ño1(û„f„²/» îþHeAª K9m²§´Á¼à‘ëqø«¾Ô"Ø?0L[RÝÏqhȪQö±ôàÿ®Ö .eŸË À¸C‹ TÙ­-·@*#ÛO¦žoô äMe Ó ½çáÚveÂô#˜b¼]T»›Æ”$|—žC$?c~šrŒiXŠÙ€?¢¸ßý ä'8ðù¤~ˆ‰æ(ÿ'p[sp}.r*"þ°€Ã`e…Ž|“òªUÖòÃ^¼€#ØÒ7“D&A°÷´^»æo˜°âuæÃ»TÀ »žN‡<.öi€.¢ÜÓÊÈIUO²å ¼{æQÏý¯a)‡MèϹè¿%¤lÝ,¨ŽEÇT¶Ñ>å”áÏù¼l©KjiMüÕV¢üFI×bê‘9ø4õÞ4ýXùp·Û©çëÁõ{n€•þ®Ò_Á«(/W¬a°Ž}‰²ÛöìmŒäø-¸n%&ÖµhO¼Âï©÷3N‹aÝÏGP¹ùâ‡1‘ÎÇ]€ú¶‚£vá~¶æìc²¸e–Tê÷${.hÉtÅ{DBúXý~/¨Gš´—±EèàÓŠ2h'j¡ véŽ@+-Hàloi®íQtDðªôÇ`5ž‡Å6ØÜ·¡sµøÙ W]‰ÙûWÜ7+¼°x;RTáÜ:tèCPïxßF£—#erIdÞ4üÖ‰¶6(4`@·ÜðÇËQ2'I´gê»AY¶§Þ•ðLi[ƒº§Q‡7•:-ŵóQæã0׃;õ‰º|Ù…1NÚ¼NI»IÛ‚§_¦{Š´§+Š[™bW?¿®žê†u乌8$ï!ùhÏx¥xõÇœ]‚ø€úÏQ‡äý*]°œ XfTFÀºŒ²ÿ&¨[þO€ùòKXWóIEND®B`‚gnatcoll-core-21.0.0/docs/filling.rst0000644000175000017500000000116413661715457017314 0ustar nicolasnicolas************************************** **Paragraph filling**: formatting text ************************************** .. index:: paragraph filling .. index:: filling .. index:: Knuth The package `GNATCOLL.Paragraph_Filling` provides several algorithms for filling paragraphs---formatting them to take up the minimal number of lines and to look better. `Knuth_Fill` is based on an algorithm invented by Donald Knuth, and used in TeX. `Pretty_Fill` uses a different algorithm, which was judged by some to produce more aesthetically pleasing output. More detailed documentation may be found in the comments in the package spec. gnatcoll-core-21.0.0/docs/geometry.rst0000644000175000017500000000222213661715457017517 0ustar nicolasnicolas******************************************** **Geometry**: primitive geometric operations ******************************************** .. highlight:: ada GNATColl provides the package `GNATCOLL.Geometry`. This package includes a number of primitive operations on geometric figures like points, segments, lines, circles, rectangles and polygons. In particular, you can compute their intersections, the distances,... This package is generic, so that you can specify the type of coordinates you wish to handle:: declare package Float_Geometry is new GNATCOLL.Geometry (Float); use Float_Geometry; P1 : constant Point := (1.0, 1.0); P2 : constant Point := (2.0, 3.0); begin Put_Line ("Distance P1-P2 is" & Distance (P1, P2)'Img); -- Will print 2.23607 end; Or some operations involving a polygon:: declare P3 : constant Point := (3.7, 2.0); P : constant Polygon := ((2.0, 1.3), (4.1, 3.0), (5.3, 2.6), (2.9, 0.7), (2.0, 1.3)); begin Put_Line ("Area of polygon:" & Area (P)); -- 3.015 Put_Line ("P3 inside polygon ? " & Inside (P3, P)'Img); -- True end; gnatcoll-core-21.0.0/docs/index.rst0000644000175000017500000000121413661715457016773 0ustar nicolasnicolasGNATColl Core Components ======================== .. toctree:: :numbered: :maxdepth: 3 intro building scripting traces strings memory mmap boyer_moore filling templates email ravenscar storage_pools vfs tribooleans geometry projects refcount config pools json terminals promises Indices and tables ================== * :ref:`genindex` This document may be copied, in whole or in part, in any form or by any means, as is or with alterations, provided that (1) alterations are clearly marked as alterations and (2) this copyright notice is included unmodified in any copy. gnatcoll-core-21.0.0/docs/refcount.rst0000644000175000017500000001217013661715457017514 0ustar nicolasnicolas******************************** **Refcount**: Reference counting ******************************** .. highlight:: ada Memory management is often a difficulty in defining an API. Should we let the user be responsible for freeing the types when they are no longer needed, or can we do it automatically on his behalf ? The latter approach is somewhat more costly in terms of efficiency (since we need extra house keeping to know when the type is no longer needed), but provides an easier to use API. Typically, such an approach is implemented using reference counting: all references to an object increment a counter. When a reference disappears, the counter is decremented, and when it finally reaches 0, the object is destroyed. .. index:: reference counting This approach is made convenient in Ada using controlled types. However, there are a number of issues to take care of to get things exactly right. In particular, the Ada Reference Manual specifies that `Finalize` should be idempotent: it could be called several times for a given object, in particular when exceptions occur. An additional difficulty is task-safety: incrementing and decrementing the counter should be task safe, since the controlled object might be referenced from several task (the fact that other methods on the object are task safe or not is given by the user application, and cannot be ensures through the reference counting mecanism). To make things easier, GNATColl provides the package `GNATCOLL.Refcount`. This package contains a generic child package. To use it, you need to create a new tagged type that extends `GNATCOLL.Refcount.Refcounted`, so that it has a counter. Here is an example:: with GNATCOLL.Refcount; use GNATCOLL.Refcount; package My_Pkg is type My_Type is new Refcounted with record Field1 : ...; -- Anything end record; package My_Type_Ptr is new Smart_Pointers (My_Type); end My_Pkg; The code above makes a `Ref` available. This is similar in semantics to an access type, although it really is a controlled type. Every time you assign the `Ref`, the counter is incremented. When the `Ref` goes out of scope, the counter is decremented, and the object is potentially freed. Here an example of use of the package:: declare R : Ref; Tmp : My_Type := ...; begin Set (R, Tmp); -- Increment counter Get (R).Field1 := ...; -- Access referenced object end; -- R out of scope, so decrement counter, and free Tmp Although reference counting solves most of the issues with memory management, it can get tricky: when there is a cycle between two reference counted objects (one includes a reference to the other, and the other a reference to the first), their counter can never become 0, and thus they are never freed. There are, however, common design patterns where this can severly interfer: imagine you want to have a `Map`, associating a name with a reference counted object. Typically, the map would be a cache of some sort. While the object exists, it should be referenced in the map. So we would like the Map to store a reference to the object. But that means the object will then never be freed while the map exists either, and memory usage will only increase. .. index:: reference, weak The solution to this issue is to use `weak references`. These hold a pointer to an object, but do not increase its counter. As a result, the object can eventually be freed. At that point, the internal data in the weak reference is reset to `null`, although the weak reference object itself is still valid. Here is an example:: with GNATCOLL.Refcount.Weakref; use GNATCOLL.Refcount.Weakref; type My_Type is new Weak_Refcounted with...; package Pointers is new Weakref_Pointers (My_Type); The above code can be used instead of the code in the first example, and provides the same capability (smart pointers, reference counted types,...). However, the type `My_Type` is slightly bigger, but can be used to create weak references:: WR : Weak_Ref; declare R : Ref; Tmp : My_Type := ...; begin Set (R, Tmp); -- Increment counter WR := Get_Weak_Ref (R); -- Get a weak reference Get (R).Field1 := ...; -- Access referenced object Get (Get (WR)).Field1 := ...; -- Access through weak ref end; -- R out of scope, so decrement counter, and free Tmp if Get (WR) /= Null_Ref then -- access to WR still valid -- Always true, since Tmp was freed end if; The example above is very simplified. Imagine, however, that you store `WR` in a map. Even when `R` is deallocated, the contents of the map remains accessible without a `Storage_Error` (although using `Get` will return `Null_Ref`, as above). For task-safety issues, `Get` on a weak-reference returns a smart pointer. Therefore, this ensures that the object is never freed while that smart pointer object lives. As a result, we recommend the following construct in your code:: declare R : constant Ref := Get (WR); begin if R /= Null_Ref then -- Get (R) never becomes null while in this block end if; end; gnatcoll-core-21.0.0/docs/intro.rst0000644000175000017500000000175313661715457017027 0ustar nicolasnicolas.. _Introduction: ********************************************* Introduction to the GNAT Component Collection ********************************************* The reusable library known as the GNAT Component Collection (GNATColl) is based on one main principle: general-purpose packages that are part of the GNAT technology should also be available to GNAT user application code. The compiler front end, the GNAT Programming Studio (GPS) Interactive Development Environment, and the GNAT Tracker web-based interface all served as sources for the components. The GNATColl components complement the predefined Ada and GNAT libraries and deal with a range of common programming issues including string and text processing, memory management, and file handling. Several of the components are especially useful in enterprise applications. Bug reports ----------- Please send questions and bug reports to report@adacore.com following the same procedures used to submit reports with the GNAT toolset itself. gnatcoll-core-21.0.0/docs/favicon.ico0000644000175000017500000000157613661715457017266 0ustar nicolasnicolash(   Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡¼“m“Q“QñéâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÞÓ“Q“QØ·¡Ø·¡ëÞÓ“Q“Qг˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼“m“Q“QØ·¡Ø·¡ÿÿÿ¡g2“Q§rAÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿš\$“Q¡g2Ø·¡Ø·¡ÿÿÿг˜“Q“QñéâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖ½§“Q“Qг˜Ø·¡Ø·¡ÿÿÿñéâ“Q“Qг˜ÿÿÿµˆ^“Q“Q“Q“Q“Q“QøôðØ·¡Ø·¡ÿÿÿÿÿÿµˆ^“Q¡g2ÿÿÿÝȶ®}P®}P®}P“Q“Qµˆ^ÿÿÿØ·¡Ø·¡ÿÿÿÿÿÿÝȶ“Q“QëÞÓÿÿÿÿÿÿÿÿÿÖ½§“Q“QÝȶÿÿÿØ·¡Ø·¡ÿÿÿÿÿÿÿÿÿš\$“QÂ|ÿÿÿÿÿÿÿÿÿµˆ^“Qš\$ÿÿÿÿÿÿØ·¡Ø·¡ÿÿÿÿÿÿÿÿÿÂ|“Q¡g2ÿÿÿÿÿÿñéâ“Q“QÂ|ÿÿÿÿÿÿØ·¡Ø·¡ÿÿÿÿÿÿÿÿÿëÞÓ“Q“QäÓÄÿÿÿг˜“Q“QñéâÿÿÿÿÿÿØ·¡Ø·¡ÿÿÿÿÿÿÿÿÿÿÿÿ§rA“Q¼“mÿÿÿ§rA“Q§rAÿÿÿÿÿÿÿÿÿØ·¡Ø·¡ÿÿÿÿÿÿÿÿÿÿÿÿÖ½§“Qš\$ëÞÓ“Q“QÖ½§ÿÿÿÿÿÿÿÿÿØ·¡Ø·¡ÿÿÿÿÿÿÿÿÿÿÿÿøôð“Q“Q§rA“Qš\$øôðÿÿÿÿÿÿÿÿÿØ·¡Ø·¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼“m“Q“Q“Q¼“mÿÿÿÿÿÿÿÿÿÿÿÿØ·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡Ø·¡le:Ph<ey <ri>fe:lolht/gBk/DAREibgnatcoll-core-21.0.0/docs/important.png0000644000175000017500000000264713661715457017670 0ustar nicolasnicolas‰PNG  IHDR"":G ÂgAMAÖØÔOX2bKGDÿÿÿ ½§“ pHYsHHFÉk> IDATXÃí—KlTUÇÿçÜ÷½ó¸wf:ÓÒ™vf -m§@ÀðªÄ -ˆ |,uAŒñ±p#k7cb0ÀNbLL ¤D1ÊK * iZ! ¢ØÖJÛ±™>èc˜÷½wîuQ m[…ü×ç|ßï|¯|x¨‡š_Ì=°!°X„x€@Ï= ÍUã Ö_px+Ðcˆ.&¼ä~&ÖúòªÆÍ;š ¡­œ <Hyݣχš¶2áU[´ÀÒÕ/…Œç?]þg+c-1Á€\ŠàŠ S–}ló+ï,ÈÞB‹Õï‹®{«þ‰×«EY‚ÓmÁíUùDïÏŽ_¿i; pÿ#B((Ã?XÞ²’“=Eš›"ZßˆÆæ- ¥­÷;"€2ÀnòD6ìŒl|©Æ©ùP]ÅÀçááP$¸44øÛE%9>r3­M˜™3ó¿o8'€SB«åC¼³2 ºƒ!Áá –·ÔT®ØÆVW‰X³R‚"3 ”À(pþä‘Â/ÃɉÉxfjt|lè÷ñ\*™°aÀÆ€m[£EÓŒ˜ºÈNå;˜²fçŠ(‚3à\¢èT‰(;¡¸\pk*TMÀÚÕ ê–É` B†n`|,‰áø4Šz–™ž™,êÙ©ÂØPoúì‘C£‰kWÞpèV§ì, ÇÁH­Ô³¾‘µ2§·ÊBu³ðh ¼*‹2/‡Šrág@ÈÍ÷°Ê ·Š\¾³hÁ²lÆ2rrjêºi[ÖQÇK©‘ Óí¤0â©{cµ«*i$L 3¨‰r¨‰Š¨®Qá— ·Cü-J 8–0QÐ LNNãÌgûÓ§Úöí‹÷¿ UR±†IZ¹‘Ròi>g,´,Êhª‡ÄA–9(Qä@él™ýË™É|6“EÇу7Î~²oÏÄpÿ{„2iØwÖîì]3s0›OŽŸK÷ù9^ˆk›Y!‰d‘»#%ÿ¼NžÏãôáýéo~°÷züê.™Ù 湩L!“ìœîó ¢ØªmdE„(°à¹¹A ¹,¾ûtêØG{w ^Ù =Ÿ£RæHFϦÎǬ¶ªfD€ãÐ9@(ô\8‡¶ÝoŽ÷u¿‰YjâŽ;%€Àd&Ï÷^ü)Ë]yèz¦iÁ¶ç›Sœ NÐKqP*ˆÌ*å5yË‹¾þ²9¹¼y ÌM Û¶aš€¤–Áé Ô(i7`K9@&¬3LExÎF^7Àæ(@$‘ËØ3E ºaS<àeg-€‰EƒF†]̺ çª"œ Ž+"“5@E6ÂX(±­† 8aè& º ÝbÁÉn€ µ<48=_ˆÚŽüÐ!"øy–³JçñGÏ%ôtœëjÿúKJ©knÙÛ´ÍjX›°(6\§heu½‰¡ü„,$w­ T^RKäkY&Rãø¾óä¿;=v­ûâiœ@}.wžy1¼býS ›¶{µêFFP—°¬ ×Ý ¢´ÔBˆŽZEƒ½Ñ} w®ã«öt¢ûcSÏí³Á¶Ï&®vè]×w±ýµèÚ'·ÊÞ fYv=fšbQß  `äÏ©»ááµWT”pG£ ÷œ¬;Ê Ò) Þeo €mÂø÷«¥ à®{òPÿoý Œ‡»n£,ñ’"zTXtSoftwarexÚsLÉOJUðÌMLO JML©/œÔ® ©MIEND®B`‚gnatcoll-core-21.0.0/docs/templates.rst0000644000175000017500000000567513661715457017701 0ustar nicolasnicolas****************************** **Templates**: generating text ****************************** .. index:: templates This module provides convenient subprograms for replacing specific substrings with other values. It is typically used to replace substrings like "%{version}" in a longer string with the actual version, at run time. This module is not the same as the templates parser provided in the context of AWS, the Ada web server, where external files are parsed and processed to generate other files. The latter provides advanced features like filters, loops,... The substrings to be replaced always start with a specific delimiter, which is set to `%` by default, but can be overridden in your code. The name of the substring to be replaced is then the identifier following that delimiter, with the following rules: * If the character following the delimiter is the delimiter itself, then the final string will contain a single instance of that delimiter, and no further substitution is done for that delimiter. An example of this is `"%%"`. * If the character immediately after the delimiter is a curly brace (`{`), then the name of the identifier is the text until the next closing curly brace. It can then contain any character expect a closing curly brace. An example of this is `"%{long name}"` * If the first character after the delimiter is a digit, then the name of the identifier is the number after the delimiter. An example of this is `"%12"`. As a special case, if the first non-digit character is the symbol `-`, it is added as part of the name of the identifier, as in `"%1-"`. One use for this feature is to indicate you want to replace it with all the positional parameters %1%2%3%4. For instance, if you are writing the command line to spawn an external tool, to which the user can pass any number of parameter, you could specify that command line as `"tool -o %1 %2-"` to indicate that all parameters should be concatenated on the command line. * If the first character after the delimiter is a letter, the identifier follows the same rules as for Ada identifiers, and can contain any letter, digit, or underscore character. An example of this is `"%ab_12"`. For readability, it is recommended to use the curly brace notation when the name is complex, but that is not mandatory. * Otherwise the name of the identifier is the single character following the delimiter For each substring matching the rules above, the `Substitute` subprogram will look for possible replacement text in the following order: * If the `Substrings` parameter contains an entry for that name, the corresponding value is used. * Otherwise, if a `callback` was specified, it is called with the name of the identifier, and should return the appropriate substitution (or raise an exception if no such substitution makes sense). * A default value provided in the substring itself * When no replacement string was found, the substring is kept unmodified gnatcoll-core-21.0.0/docs/boyer_moore.rst0000644000175000017500000000477413661715457020223 0ustar nicolasnicolas.. _Searching_strings: ********************************** **Boyer-Moore**: Searching strings ********************************** .. highlight:: ada .. index:: Boyer-Moore .. index:: search Although the Ada standard provides a number of string-searching subprograms (most notably in the `Ada.Strings.Fixed`, `Ada.Strings.Unbounded` and `Ada.Strings.Bounded` packages through the `Index` functions), these subprograms do not in general provide the most efficient algorithms for searching strings. The package **GNATCOLL.Boyer_Moore** provides one such optimize algorithm, although there exists several others which might be more efficient depending on the pattern. It deals with string searching, and does not handle regular expressions for instance. This algorithm needs to preprocess its key (the searched string), but does not need to perform any specific analysis of the string to be searched. Its execution time can be sub-linear: it doesn't need to actually check every character of the string to be searched, and will skip over some of them. The worst case for this algorithm has been proved to need approximately 3 * N comparisons, hence the algorithm has a complexity of O(n). The longer the key, the faster the algorithm in general, since that provides more context as to how many characters can be skipped when a non-matching character is found.. We will not go into the details of the algorithm, although a general description follows: when the pattern is being preprocessed, Boyer-Moore computes how many characters can be skipped if an incorrect match is found at that point, depending on which character was read. In addition, this algorithm tries to match the key starting from its end, which in general provides a greater number of characters to skip. For instance, if you are looking for "ABC" in the string "ABDEFG" at the first position, the algorithm will compare "C" and "D". Since "D" does not appear in the key "ABC", it knows that it can immediately skip 3 characters and start the search after "D". Using this package is extremely easy, and it has only a limited API:: declare Str : constant String := "ABDEABCFGABC"; Key : Pattern; Index : Integer; begin Compile (Key, "ABC"); Index := Search (Key, Str); end `Search` will either return -1 when the pattern did not match, or the index of the first match in the string. In the example above, it will return 5. If you want to find the next match, you have to pass a substring to search, as in:: Index := Search (Key, Str (6 .. Str'Last)); gnatcoll-core-21.0.0/docs/email.rst0000644000175000017500000001552213661715457016762 0ustar nicolasnicolas************************************ **Email**: Processing email messages ************************************ .. highlight:: ada .. index:: email GNATColl provides a set of packages for managing and processing email messages. Through this packages, you can extract the various messages contained in an existing mailbox, extract the various components of a message, editing previously parsed messages, or create new messages from scratch. This module fully supports MIME-encoded messages, with attachments. This module currently does not provide a way to send the message through the SMTP protocol. Rather, it is used to create an in-memory representation of the message, which you can then convert to a string, and pass this to a socket. See for instance the `AWS library `_) which contains the necessary subprograms to connect with an SMTP server. Message formats =============== .. index:: GNATCOLL.Email.Utils The format of mail messages is defined through numerous RFC documents. GNATColl tries to conform to these as best as possible. Basically, a message is made of two parts: *The headers* These are various fields that indicate who sent the message, when, to whom, and so on *The payload (aka body)* This is the actual contents of the message. It can either be a simple text, or made of one or more attachments in various formats. These attachments can be HTML text, images, or any binary file. Since email transfer is done through various servers, the set of bytes that can be sent is generally limited to 7 bit characters. Therefore, the attachments are generally encoded through one of the encoding defined in the various MIME RFCs, and they need to be decoded before the original file can be manipulated again. GNATColl gives you access to these various components, as will be seen in the section :ref:`Parsing_messages`. .. index:: MIME .. index:: encoding The package :file:`GNATCOLL.Email.Utils` contains various subprograms to decode MIME-encoded streams, which you can use independently from the rest of the packages in the email module. The headers part of the message contains various pieces of information about the message. Most of the headers have a well-defined semantics and format. However, a user is free to add new headers, which will generally start with `X-` prefix. For those fields where the format is well-defined, they contain various pieces of information: *Email addresses* The `From`, `TO` or `CC` fields, among others, contain list of recipients. These recipients are the usual email addresses. However, the format is quite complex, because the full name of the recipient can also be specified, along with comments. The package :file:`GNATCOLL.Email.Utils` provides various subprograms for parsing email addresses and list of recipients. *Dates* The `Date` header indicates when the message was sent. The format of the date is also precisely defined in the RFC, and the package :file:`GNATCOLL.Email.Utils` provides subprograms for parsing this date (or, on the contrary, to create a string from an existing time). *Text* The `Subject` header provides a brief overview of the message. It is a simple text header. However, one complication comes from the fact that the user might want to use extended characters not in the ASCII subset. In such cases, the Subject (or part of it) will be MIME-encoded. The package :file:`GNATCOLL.Email.Utils` provides subprograms to decode MIME-encoded strings, with the various charsets. .. _Parsing_messages: Parsing messages ================ There are two ways a message is represented in memory: initially, it is a free-form `String`. The usual Ada operations can be used on the string, of course, but there is no way to extract the various components of the message. For this, the message must first be parsed into an instance of the `Message` type. This type is controlled, which means that the memory will be freed automatically when the message is no longer needed. .. index:: GNATCOLL.Email.Parser The package :file:`GNATCOLL.Email.Parser` provides various subprograms that parse a message (passed as a string), and create a `Message` out of it. Parsing a message might be costly in some cases, for instance if a big attachment needs to be decoded first. In some cases, your application will not need that information (for instance you might only be looking for a few of the headers of the message, and not need any information from the body). This efficiency concern is why there are multiple parsers. Some of them will ignore parts of the message, and thus be more efficient if you can use them. .. index:: GNATCOLL.Email Once a `Message` has been created, the subprograms in `GNATCOLL.Email` can be used to access its various parts. The documentation for these subprograms is found in the file `gnatcoll-email.ads` directly, and is not duplicated here. Parsing mailboxes ================= Most often, a message is not found on its own (unless you are for instance writing a filter for incoming messages). Instead, the messages are stored in what is called a mailbox. The latter can contain thousands of such messages. There are traditionally multiple formats that have been used for mailboxes. At this stage, GNATColl only supports one of them, the `mbox` format. In this format, the messages are concatenated in a single file, and separated by a newline. .. index:: GNATCOLL.Email.Mailboxes The package `GNATCOLL.Email.Mailboxes` provides all the types and subprograms to manipulate mailboxes. Tagged types are used, so that new formats of mailboxes can relatively easily be added later on, or in your own application. Here is a small code example that opens an mbox on the disk, and parses each message it contains:: declare Box : Mbox; Curs : Cursor; Msg : Message; begin Open (Box, Filename => "my_mbox"); Curs := Mbox_Cursor (First (Box)); while Has_Element (Curs) loop Get_Message (Curs, Box, Msg); if Msg /= Null_Message then ... end if; Next (Curs, Box); end loop; end; As you can see, the mailbox needs to be opened first. Then we get an iterator (called a cursor, to match the Ada2005 containers naming scheme), and we then parse each message. The `if` test is optional, but recommended: the message that is returned might be null if the mailbox was corrupted and the message could not be parsed. There are still chances that the next message will be readable, so only the current message should be ignored. Creating messages ================= The subprograms in `GNATCOLL.Email` can also be used to create a message from scratch. Alternatively, if you have already parsed a message, you can alter it, or easily generate a reply to it (using the `Reply_To` subprogram. The latter will preset some headers, so that message threading is preserved in the user's mailers. gnatcoll-core-21.0.0/docs/pools.rst0000644000175000017500000000302713661715457017024 0ustar nicolasnicolas****************************************** **Pools**: Controlling access to resources ****************************************** The package **GNATCOLL.Pools** provides resource pools. A pool contains a maximum number of resources, which are created on demand. However, once a resource is no longer needed by the client, it is not freed, but instead it is released to the pool, which will then return it again the next time a client requests a resource. The typical resource is when the creation of the resources is expensive, for instance a connection to a database or a remote server. The lazy creation then provides a faster startup time (as well as more flexibility, since there is no need to allocate dozens of resources if only one will be needed in the end), and more efficient retrieval through the reuse of resources. The pool in this package is task safe, and is intended as a global variable (or field of a global variable) somewhere in your application. The resources are implemented as reference-counted types (through `GNATCOLL.Refcount`). As a result, as soon as the client no longer has a handle on them, they are automatically released to the pool and there is no risk that the client forgets to do so. `GNATCOLL.Pools` is a generic package where the formal parameters describe the type of resources, how to create them on demand, what should happen when a resource is released, and finally how to free a resource when the pool itself is freed. See :file:`gnatcoll-pools.ads` for a full and up-to-date description of these parameters. gnatcoll-core-21.0.0/docs/storage_pools.rst0000644000175000017500000000456313661715457020556 0ustar nicolasnicolas************************************************ **Storage Pools**: controlling memory management ************************************************ Ada gives full control to the user for memory management. That allows for a number of optimization in your application. For instance, if you need to allocate a lot of small chunks of memory, it is generally more efficient to allocate a single large chunk, which is later divided into smaller chunks. That results in a single system call, which speeds up your application. This can of course be done in most languages. However, that generally means you have to remember not to use the standard memory allocations like `malloc` or `new`, and instead call one of your subprograms. If you ever decide to change the allocation strategy, or want to experiment with several strategies, that means updating your code in several places. In Ada, when you declare the type of your data, you also specify through a `'Storage_Pool` attribute how the memory for instances of that type should be allocated. And that's it. You then use the usual `new` keyword to allocate memory. GNATColl provides a number of examples for such storage pools, with various goals. There is also one advanced such pool in the GNAT run-time itself, called `GNAT.Debug_Pools`, which allows you to control memory leaks and whether all accesses do reference valid memory location (and not memory that has already been deallocated). In GNATColl, you will find the following storage pools: *`GNATCOLL.Storage_Pools.Alignment`* This pool gives you full control over the alignment of your data. In general, Ada will only allow you to specify alignments up to a limited number of bytes, because the compiler must only accept alignments that can be satisfied in all contexts, in particular on the stack. This package overcomes that limitation, by allocating larger chunks of memory than needed, and returning an address within that chunk which is properly aligned. *`GNATCOLL.Storage_Pools.Headers`* This pool allows you to allocate memory for the element and reserve extra space before it for a header. This header can be used to store per-element information, like for instance a reference counter, or next and previous links to other elements in the same collection. In many cases, this can be used to reduce the number of allocations, and thus speed up the overall application. gnatcoll-core-21.0.0/docs/traces.rst0000644000175000017500000005565313661715457017165 0ustar nicolasnicolas.. highlight:: ada .. _Logging_information: ******************************* **Traces**: Logging information ******************************* Most applications need to log various kinds of information: error messages, information messages or debug messages among others. These logs can be displayed and stored in a number of places: standard output, a file, the system logger, an application-specific database table,... The package :file:`GNATCOLL.Traces` addresses the various needs, except for the application-specific database, which of course is specific to your business and needs various custom fields in any case, which cannot be easily provided through a general interface. This module is organized around two tagged types (used through access types, in fact, so the latter are mentioned below as a shortcut): *Trace_Handle* This type defines a handle (similar to a file descriptor in other contexts) which is latter used to output messages. An application will generally define several handles, which can be enabled or disabled separately, therefore limiting the amount of logging. *Trace_Stream* Streams are the ultimate types responsible for the output of the messages. One or more handles are associated with each stream. The latter can be a file, the standard output, a graphical window, a socket,... New types of streams can easily be defined in your application. .. _Configuring_traces: Configuring traces ================== As mentioned above, an application will generally create several `Trace_Handle` (typically one per module in the application). When new features are added to the application, the developers will generally need to add lots of traces to help investigate problems once the application is installed at a customer's site. The problem here is that each module might output a lot of information, thus confusing the logs; this also does not help debugging. The `GNATCOLL.Traces` package allows the user to configure which handles should actually generate logs, and which should just be silent and not generate anything. Depending on the part of the application that needs to be investigated, one can therefore enable a set of handles or another, to be able to concentrate on that part of the application. This configuration is done at two levels: * either in the source code itself, where some `trace_handle` might be disabled or enabled by default. This will be described in more details in later sections. * or in a configuration file which is read at runtime, and overrides the defaults set in the source code. The configuration file is found in one of three places, in the following order: * The file name is specified in the source code in the call to `Parse_Config_File`. .. index:: ADA_DEBUG_FILE * If no file name was specified in that call, the environment variable `ADA_DEBUG_FILE` might point to a configuration file. .. index:: .gnatdebug * If the above two attempts did not find a suitable configuration file, the current directory is searched for a file called `.gnatdebug`. Finally, the user's home directory will also be searched for that file. In all cases, the format of the configuration file is the same. Its goal is to associate the name of a `trace_handle` with the name of a `trace_stream` on which it should be displayed. Streams are identified by a name. You can provide additional streams by creating a new tagged object (:ref:`Defining_custom_stream_types`). Here are the various possibilities to reference a stream: *"name"* where name is a string made of letters, digits and slash ('/') characters. This is the name of a file to which the traces should be redirected. The previous contents of the file is discarded. If the name of the file is a relative path, it is relative to the location of the configuration file, not necessarily to the current directory when the file is parsed. In the file name, `$$` is automatically replaced by the process number. `$D` is automatically replaced by the current date. `$T` is automatically replaced by the current date and time. Other patterns of the form `$name`, `${name}`, or `$(name)` are substituted with the value of the named environment variable, if it exists. If ">>" is used instead of ">" to redirect to that stream, the file is appended to, instead of truncated. *"&1"* This syntax is similar to the one used on Unix shells, and indicates that the output should be displayed on the standard output for the application. If the application is graphical, and in particular on Windows platforms, it is possible that there is no standard output! *"&2"* Similar to the previous one, but the output is sent to standard error. *"&syslog"* :ref:`Logging_to_syslog`. Comments in a configuration file must be on a line of their own, and start with `--`. Empty lines are ignored. The rest of the lines represent configurations, as in: * If a line contains the single character `"+"`, it activates all `trace_handle` by default. This means the rest of the configuration file should disable those handles that are not needed. The default is that all handles are disabled by default, and the configuration file should activate the ones it needs. The Ada source code can change the default status of each handles, as well * If the line starts with the character `">"`, followed by a stream name (as defined above), this becomes the default stream. All handles will be displayed on that stream, unless otherwise specified. If the stream does not exist, it defaults to standard output. * Otherwise, the first token on the line is the name of a handle. If that is the only element on the line, the handle is activated, and will be displayed on the default stream. Otherwise, the next element on the line should be a `"="` sign, followed by either `"yes"` or `"no"`, depending on whether the handle should resp. be enabled or disabled. Finally, the rest of the line can optionally contain the `">"` character followed by the name of the stream to which the handle should be directed. There is are two special cases for the names on this line: they can start with either `"\*."` or `".\*"` to indicate the settings apply to a whole set of handles. See the example below. Here is a short example of a configuration file. It activates all handles by default, and defines four handles: two of them are directed to the default stream (standard error), the third one to a file on the disk, and the last one to the system logger syslog (if your system supports it, otherwise to the default stream, ie standard error):: + >&2 MODULE1 MODULE2=yes SYSLOG=yes >&syslog:local0:info FILE=yes >/tmp/file -- decorators (see below) DEBUG.COLORS=yes -- Applies to FIRST.EXCEPTIONS, LAST.EXCEPTIONS,... -- and forces them to be displayed on stdout *.EXCEPTIONS=yes > stdout -- Applies to MODULE1, MODULE1.FIRST,... This can be used to -- disable a whole hierarchy of modules. -- As always, the latest config overrides earlier ones, so the -- module MODULE1.EXCEPTIONS would be disabled as well. MODULE1.*=no .. _Using_the_traces_module: Using the traces module ======================= If you need or want to parse an external configuration file as described in the first section, the code that initializes your application should contain a call to `GNATCOLL.Traces.Parse_Config_File`. As documented, this takes in parameter the name of the configuration file to parse. When none is specified, the algorithm specified in the previous section will be used to find an appropriate configuration:: GNATCOLL.Traces.Parse_Config_File; The code, as written, will end up looking for a file :file:`.gnatdebug` in the current directory. The function :code:`Parse_Config_File` must be called to indicate that you want to activate the traces. It must also end up finding a configuration file. If it does not, then none of the other functions will ever output anything. This is to make sure your application does not start printing extra output just because you happen to use an external library that uses :code:`GNATCOLL.Traces`. It also ensures that your application will not try to write to :code:`stdout` unless you think it is appropriate (since :code:`stdout` might not even exist in fact). You then need to declare each of the `trace_handle` (or `logger`) that your application will use. The same handle can be declared several times, so the recommended approach is to declare locally in each package body the handles it will need, even if several bodies actually need the same handle. That helps to know which traces to activate when debugging a package, and limits the dependencies of packages on a shared package somewhere that would contain the declaration of all shared handles. .. index:: Trace_Handle .. index:: Logger Function Trace_Handle Create Name Default Stream Factory Finalize This function creates (or return an existing) a `trace_handle` with the specified `Name`. Its default activation status can also be specified (through `Default`), although the default behavior is to get it from the configuration file. If a handle is created several times, only the first call that is executed can define the default activation status, the following calls will have no effect. `Stream` is the name of the stream to which it should be directed. Here as well, it is generally better to leave things to the configuration file, although in some cases you might want to force a specific behavior. `Factory` is used to create your own child types of `trace_handle` (:ref:`Log_decorators`). Here is an example with two package bodies that define their own handles, which are later used for output:: package body Pkg1 is Me : constant Trace_Handle := Create ("PKG1"); Log : constant Trace_Handle := Create ("LOG", Stream => "@syslog"); end Pkg1; package body Pkg2 is Me : constant Trace_Handle := Create ("PKG2"); Log : constant Trace_Handle := Create ("LOG", Stream => "@syslog"); end Pkg2; Once the handles have been declared, output is a matter of calling the `GNATCOLL.Traces.Trace` procedure, as in the following sample:: Trace (Me, "I am here"); An additional subprogram can be used to test for assertions (pre-conditions or post-conditions in your program), and output a message whether the assertion is met or not:: Assert (Me, A = B, "A is not equal to B"); If the output of the stream is done in color, a failed assertion is displayed with a red background to make it more obvious. Logging unexpected exceptions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A special version of `Trace` is provided, which takes an `Exception_Occurrence` as argument, and prints its message and backtrace into the corresponding log stream. This procedure will in general be used for unexcepted exceptions. Since such exceptions should be handled by developers, it is possible to configure `GNATCOLL.TRACES` to use special streams for those. `Trace (Me, E)` will therefore not used `Me` itself as the log handle, but will create (on the fly, the first time) a new handle with the same base name and and `.EXCEPTIONS` suffix. Therefore, you could put the following in your configuration file:: # Redirect all exceptions to stdout *.EXCEPTIONS=yes >& stdout and then the following code will output the exception trace to stdout:: procedure Proc is Me : Create ("MYMODULE"); begin ... exception when E : others => Trace (Me, E, Msg => "unexcepted exception:"); end Proc; Checking whether the handle is active ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As we noted before, handles can be disabled. In that case, your application should not spend time preparing the output string, since that would be wasted time. In particular, using the standard Ada string concatenation operator requires allocating temporary memory. It is therefore recommended, when the string to display is complex, to first test whether the handle is active. This is done with the following code:: if Active (Me) then Trace (Me, A & B & C & D & E); end if; .. _Log_decorators: Log decorators ============== .. index:: decorator, log Speaking of color, a number of decorators are defined by `GNATCOLL.Traces`. Their goal is not to be used for outputting information, but to configure what extra information should be output with all log messages. They are activated through the same configuration file as the traces, with the same syntax (i.e either `"=yes"` or `"=no"`). Here is an exhaustive list: *DEBUG.ABSOLUTE_TIME* If this decorator is activated in the configuration file, the absolute time when Trace is called is automatically added to the output, when the streams supports it (in particular, this has no effect for syslog, which already does this on its own). *DEBUG.MICRO_TIME* If active, the time displayed by DEBUG.ABSOLUTE_TIME will use a microseconds precision, instead of milliseconds. *DEBUG.ELAPSED_TIME* If this decorator is activated, then the elapsed time since the last call to Trace for the same handle is also displayed. *DEBUG.STACK_TRACE* If this decorator is activated, then the stack trace is also displayed. It can be converted to a symbolic stack trace through the use of the external application `addr2line`, but that would be too costly to do this automatically for each message. *DEBUG.LOCATION* If this decorator is activated, the location of the call to Trace is automatically displayed. This is a file:line:column information. This works even when the executable wasn't compiled with debug information *DEBUG.ENCLOSING_ENTITY* Activate this decorator to automatically display the name of the subprogram that contains the call to `Trace`. *DEBUG.COLORS* If this decorator is activated, the messages will use colors for the various fields, if the stream supports it (syslog doesn't). *DEBUG.COUNT* This decorator displays two additional numbers on each line: the first is the number of times this handle was used so far in the application, the second is the total number of traces emitted so far. These numbers can for instance be used to set conditional breakpoints on a specific trace (break on `gnat.traces.log` or `gnat.traces.trace` and check the value of `Handle.Count`. It can also be used to refer to a specific line in some comment file. *DEBUG.MEMORY* Every time a message is output, display the amount of memory currently in use by the application. *DEBUG.SPLIT_LINES* When this is enabled, messages are split at each newline character. Each line then starts with the name of the logger, indentation level and so on. This might result in more readable output, but is slightly slower. *DEBUG.FINALIZE_TRACES* This handle is activated by default, and indicates whether `GNATCOLL.Traces.Finalize` should have any effect. This can be set to False when debugging, to ensure that traces are available during the finalization of your application. Here is an example of output where several decorators were activated. In this example, the output is folded on several lines, but in reality everything is output on a single line:: [MODULE] 6/247 User Message (2007-07-03 13:12:53.46) (elapsed: 2ms)(loc: gnatcoll-traces.adb:224) (entity:GNATCOLL.Traces.Log) (callstack: 40FD9902 082FCFDD 082FE8DF ) Depending on your application, there are lots of other possible decorators that could be useful (for instance the current thread, or the name of the executable when you have several of them,...). Since `GNATCOLL.Traces` cannot provide all possible decorators, it provides support, through tagged types, so that you can create your own decorators. This needs you to override the `Trace_Handle_Record` tagged type. Since this type is created through calls to `GNATCOLL.Traces.Create`. This is done by providing an additional `Factory` parameter to `Create`; this is a function that allocates and returns the new handle. Then you can override either (or both) of the primitive operations `Pre_Decorator` and `Post_Decorator`. The following example creates a new type of handles, and prints a constant string just after the module name:: type My_Handle is new Trace_Handle_Record with null record; procedure Pre_Decorator (Handle : in out My_Handle; Stream : in out Trace_Stream_Record'Class; Message : String) is begin Put (Stream, "TEST"); Pre_Decorator (Trace_Handle_Record (Handle), Stream, Message); end**; function Factory return Trace_Handle is begin return new My_Handle; end; Me : Trace_Handle := Create ("MODULE", Factory => Factory'Access); As we will see below (:ref:`Dynamically_disabling_features`), you can also make all or part of your decorators conditional and configurable through the same configuration file as the trace handles themselves. .. _Defining_custom_stream_types: Defining custom stream types ============================ We noted above that several predefined types of streams exist, to output to a file, to standard output or to standard error. Depending on your specific needs, you might want to output to other media. For instance, in a graphical application, you could have a window that shows the traces (perhaps in addition to filing them in a file, since otherwise the window would disappear along with its contents if the application crashes); or you could write to a socket (or even a CORBA ORB) to communicate with another application which is charge of monitoring your application. You do not need the code below if you simply want to have a new stream in your application (for instance using one for logging Info messages, one for Error messages, and so on). In this case, the function `Create` is all you need. `GNATCOLL.Traces` provides the type `Trace_Stream_Record`, which can be overridden to redirect the traces to your own streams. Let's assume for now that you have defined a new type of stream (called `"mystream"`). To keep the example simple, we will assume this stream also redirects to a file. For flexibility, however, you want to let the user configure the file name from the traces configuration file. Here is an example of a configuration file that sets the default stream to a file called :file:`foo`, and redirects a specific handle to another file called :file:`bar`. Note how the same syntax that was used for standard output and standard error is also reused (ie the stream name starts with the `"&"` symbol, to avoid confusion with standard file names):: >&mystream:foo MODULE=yes >&mystream:bar You need of course to do a bit of coding in Ada to create the stream. This is done by creating a new child of `Trace_Stream_Record`, and override the primitive operation `Put`. The whole output message is given as a single parameter to `Put`:: type My_Stream is new Trace_Stream_Record with record File : access File_Type; end record; procedure Put (Stream : in out My_Stream; Str : Msg_Strings.XString) is S : Msg_Strings.Unconstrained_String_Access; L : Natural; begin Str.Get_String (S, L); Put (Stream.File.all, String (S (1 .. L))); end Put; The above code did not open the file itself, as you might have noticed, nor did it register the name `"mystream"` so that it can be used in the configuration file. All this is done by creating a factory, ie a function in charge of creating the new stream. A factory is also a tagged object (so that you can store custom information in it), with a single primitive operation, `New_Stream`, in charge of creating and initializing a new stream. This operation receives in parameter the argument specified by the user in the configuration file (after the `":"` character, if any), and must return a newly allocated stream. This function is also never called twice with the same argument, since `GNATCOLL.Traces` automatically reuses an existing stream when one with the same name and arguments already exists:: type My_Stream_Factory is new Stream_Factory with null record; overriding function New_Stream (Self : My_Stream_Factory; Args : String) return Trace_Stream is Str : access My_Stream := new My_Stream; begin Str.File := new File_Type; Open (Str.File, Out_File, Args); return Str; end Factory; Fact : access My_Stream_Factory := new My_Stream_Factory; Register_Stream_Factory ("mystream", Fact); .. _Logging_to_syslog: Logging to syslog ================= .. index:: syslog .. index:: gnat.traces.syslog Among the predefined streams, GNATColl gives access to the system logger `syslog`. This is a standard utility on all Unix systems, but is not available on other systems. When you compile GNATColl, you should specify the switch `--enable-syslog` to configure to activate the support. If either this switch wasn't specified, or configure could not find the relevant header files anyway, then support for `syslog` will not be available. In this case, the package `GNATCOLL.Traces.Syslog` is still available, but contains a single function that does nothing. If your configuration files redirect some trace handles to `"syslog"`, they will instead be redirect to the default stream or to standard output. Activating support for syslog requires the following call in your application:: GNATCOLL.Traces.Syslog.Register_Syslog_Stream; This procedure is always available, whether your system supports or not syslog, and will simply do nothing if it doesn't support syslog. This means that you do not need to have conditional code in your application to handle that, and you can let GNATColl take care of this. After the above call, trace handles can be redirected to a stream named `"syslog"`. The package `GNATCOLL.Traces.Syslog` also contains a low-level interface to syslog, which, although fully functional, you should probably not use, since that would make your code system-dependent. Syslog itself dispatches its output based on two criteria: the `facility`, which indicates what application emitted the message, and where it should be filed, and the `level` which indicates the urgency level of the message. Both of these criteria can be specified in the `GNATCOLL.Traces` configuration file, as follows:: MODULE=yes >&syslog:user:error The above configuration will redirect to a facility called `user`, with an urgency level `error`. See the enumeration types in :file:`gnatcoll-traces-syslog.ads` for more information on valid facilities and levels. .. _Dynamically_disabling_features: Dynamically disabling features ============================== Although the trace handles are primarily meant for outputting messages, they can be used in another context. The goal is to take advantage of the external configuration file, without reimplementing a similar feature in your application. Since the configuration file can be used to activated or de-activated a handle dynamically, you can then have conditional sections in your application that depends on that handle, as in the following example:: CONDITIONAL=yes and in the Ada code:: package Pkg is Me : constant Trace_Handle := Create ("CONDITIONAL"); begin if Active (Me) then ... conditional code end if; end Pkg; In particular, this can be used if you write your own decorators, as explained above. gnatcoll-core-21.0.0/docs/json.rst0000644000175000017500000000754613661715457016653 0ustar nicolasnicolas**************************** **JSON**: handling JSON data **************************** .. index:: json .. highlight:: ada `JSON `_ is a format often used on the web to communicate between a server and a browser, or between servers. It plays a similar role to XML, but it has a much lighter syntax. On the other hand, it doesn't provide advanced features like validation, which XML provides. The ``GNATCOLL.JSON`` package provides an Ada API to decode JSON data from strings and to encode that data back to strings. It also allows one to create and modify JSON data. API overview ============ The entry point for this API is the ``JSON_Value`` data type. JSON values can be any of: * a null value (``JSON_Null_Type``): all such JSON values are equivalent; * a boolean value (``JSON_Boolean_Type``): either true or false; * an integer value (``JSON_Int_Type``), they are encoded as an Ada ``Long_Long_Integer``; * a floating point value (``JSON_Float_Type``), they are encoded as an Ada ``Long_Float``; * an UTF-8 encoded string (``JSON_String_Type``); * an array of JSON values (``JSON_Array_Type``); * a JSON object (``JSON_Object_Type``), which is a sequence of fields. Each field has a unique name and maps to a JSON value. Depending on the context, this sequence can be processed as a mapping, because each field name is unique, but iterating on fields is deterministic because it is a sequence underneath. Parsing JSON is as easy as calling the ``Read`` function:: Data : JSON_Value := Read ("[1, ""foo"", {""foo"": null}]"); Encoding to JSON is not any more complex:: JSON_String : String := Write (Data); JSON trees (``JSON_Value``) are available for both inspection and modification:: Float_Number : JSON_Value := Create (Float'(1.0)); -- Mere float number Object : JSON_Value := Get (Get (Data), 3); -- JSON object from Data: {"foo": null} Some_Array : JSON_Value := Create (Float_Number & Object & Create (False)); -- Synthetic JSON array: [1.0, {"foo": null}, False] -- Modify Data in place Data.Append (Some_Array); Examples ======== Here is a complete program demonstrating the use of this API:: with Ada.Text_IO; use Ada.Text_IO; with GNATCOLL.JSON; use GNATCOLL.JSON; procedure JSON_Test is -- Create a JSON value from scratch My_Obj : JSON_Value := Create_Object; begin My_Obj.Set_Field ("field1", Create (1)); My_Obj.Set_Field ("name", "theName"); -- Now serialize it. The call below will display: -- {"field1": 1, "name": "thename"} Put_Line (My_Obj.Write); end JSON_Test; The above uses the Ada 2005 "dot notation" to call primitive operations (``.Set_Field``, ``.Write``), but naturally the more traditional "prefix notation" is also available:: Set_Field (My_Obj, "field1", Create (1)); It is also possible to create JSON arrays. These are not tagged types, so the prefix notation has to be used. Here is a further example that sets another field in the object we had before (``My_Obj``):: declare -- Create a JSON array My_Arr : JSON_Array := Empty_Array; begin -- Fill it Append (My_Arr, Create (1)); Append (My_Arr, Create ("aString")); -- Create a field in My_Obj to hold this array My_Obj.Set_Field ("vals", My_Arr); -- This will now display: -- {"field1": 1, "name": "thename", "vals": [1, "aString"]} Put_Line (My_Obj.Write); end; Similarly to containers from the standard Ada library (from ``Ada.Containers``), ``GNATCOLL.JSON`` features automatic memory management. This means that there is no need for explicit destructors. The above is all that is needed for most uses of ``GNATCOLL.JSON``. To know more about its API, please refer to the `gnatcoll-json.ads `_ source file. gnatcoll-core-21.0.0/docs/classes.xmi0000644000175000017500000005722213661715457017320 0ustar nicolasnicolas umbrello uml modeller http://uml.sf.net 1.5.7 UnicodeUTF8 gnatcoll-core-21.0.0/docs/promises.rst0000644000175000017500000000225213661715457017530 0ustar nicolasnicolas.. highlight:: ada ************************************* **Promises**: deferring work ************************************* This package provides a way to synchronize work between some asynchronous workers (or threads). Promises are a way to encapsulate a yet unknown value, immediately return to the caller, and work in the background to actually execute the work. For instance, you could have a function that reads some data from a socket. This takes time, and we do not want to block the application while retrieving the data (and if there is an error retrieving it, we certainly want to properly handle it). The main thread (for instance a graphical user interface) needs to keep processing events and refresh itself. As soon as the data becomes available from the socket, we should let this main thread know so that it can take further action, like post-processing the data and then displaying it. A general scheme to do that is to have a callback function that is called whenever the work is finished. Promises build on that simple idea so that you can easily chain multiple callbacks to build more complex actions. See the extensive documentation in :file:`gnatcoll-promises.ads` gnatcoll-core-21.0.0/docs/classes.png0000644000175000017500000007143113661715457017305 0ustar nicolasnicolas‰PNG  IHDR}φ& IDATxœìg\Ë×Ç'@èH‘&]¤Y)¢bƒ ]l(ED/vQÄvÅ‚í*övízUT¬ˆŠ b±¡‚( Š‚t¤†yŠM¡PhªªÎ&¬µÑÐp}øðBÈÌlB¨ºº–B¡ÉÈ8655mÞ|^]ÝEHhœššóŽ—šuïìÙ?>Üëéi‹úøñ–•`f¶HTÔF^~’Ÿß‘ÆF'Bwï¾22ò¶VVvZ¾üH]]ѪªÖ¹à|&µ€UÇj …F¡ÐÂÂ^"„®^}J¡Ð¬­W´ýWüÁÓ§ï--—KI9P©Vêê.S¦¬old4[êñãx …¦¦æÌ%O~~ …B“”´ç½HKÁª Phùù%mh ú»äê „22òi4_11{ûáýå=`€VJJ†­íªúúF77KEEÙÇãËʪBß¾åìÝ{ÔTII…¦à;/gÍÚ‘²pádÿSÂÂÔg‰Š ;vgË–óFF:‹OÉÌ,øü9“‹ŠÊ’’¾#„ ê‹úô)ÃÁaMïÞÒûö-¾~=úàÁUU…1c“:˜˜îä´ADDxÉ’)11I‡…Ô×7  …scÜ8Vu8ŸããÓˆfͲco WWËë×£n܈ž4iô­[ÏBXè~tä Duu ¿K΄ #ôQSS@ÕÔÔݺõ|̘¥tzù™3uu 6W®:´ôÓ§ó::ª! ¥ììã©©©>Δ¦¦ÒÞ½‹nÞÜÖ«—DAAijjÖìÙö!*UpÃÏU«ÜRR2°Z$%ÅV­r»xq}³î)(LVTœòþ}Ú˜1ƒ}}§!„þù'¢®®ÁÇÇÉËË~ýz„ÐíÛÏ99ô€ÁhZ´hò>7nlF?ÿ 1ñ7p>“ZhhhdoŠC‡– „…½ª¨¨¾ÿ„„è´i­ÿJ€éÈ2ö(¬ˆŽÎÌÅ‹HJÚ«©9?yòséèÑ[}úL•’r3ƇB¡ãÓÒ“ÊÉ¡›˜Ì“”´§R­´µg`Cq-­뛚ÎG}ùò“ÕS'5ÎÅmâxÞo|] Ä]Žøú:geÝøôéü¶mÞ ¥´´òéÓ÷ÙÙE!=\f11aUUyŽí)(( ''…ªªªÁš>}œ°0555kÙ²Ãzz¼Œ3»¸ÐBK–8II‰#„²² BëÖ–’r°³[*,,ãä@^^1BH[»BHSS!TSSgk;Œw7H-””T²7…²²œÝðŠŠê5kNVVþš:Õ\BB´ÙS€®û™¬ì„©S7bÿ§±²˜˜¤É“GÏëXRRÉ>BV]]K4… PÙÛÏÊ*œ5kGc#cáÂÉ!aaê¶mÞ6Ì"IOÏ=~üNuumN}õêã¡çÏ“||”ÚÚãr_âNeå/„ТE“ýü\ëן¹ÿM[ÕHjœ“lBèçÏ„˜˜È”)c¸¸ó™Ô×ÙÁzɧNÝEyyÙ· ½ KÑ‘#dÄÚUTäóòBoßÞŽJOÏE]»…òò²»ysëºu­;) ÷ׯ?ùù%55u¦¦ú _û÷×äÅÃg=~÷òåGW×ͯ_ûãñ'N„]¼øðׯZYY©ÌÌ&“¹fÍ R¼¼ì޽uôèí_¿ê^¾LFÍžm¿téa.nÈÊJ²ûLjJÅ_N“&–“ëURR¡®®hiiÄ{û@—Ã××Ù××9%%#4ôy@ÀY^FȸXè**ª«ªjš}”+%%¦¬,gl¬ƒb0¡üübô_/¼Õ\¸éå(((`iiœ“Cÿü9³©‰ÙV5’çd„5ž·nÝi,7ž×µ€þ.9..´ñãÍèôòÐИüüww›gω‹‹ö‹ŒÜ3zô {÷Þœ8&++%+‹ïäጉIüòå§••É… ëBÂÂÔ½{+(È\ºôèòåÇÆéÇÆ¦œ½v-ÊÉiìõë›yñPPP 8x£´´DBÂ×åËÿ62Ò‰ŒÜcb¢{ãFôáÃ7?}úA£qrÀÄDïÖ­¿tuUOº››[ìëë¼ÿînà|&µ@tRD„jf6!äînÝêÁ.à:r„Œì?19¹^è¿pÕjîÞ}…Ú¶ÍûÑ£}ææ†­«Q@€‚úù³°¬¬ œãbœ“‘fÇóºÐß%ÇÂÂЂä:CÑhFÏŸÿÍžÂd{ÍßÌlóßúWT”IN>‡3âããäããÄÚõövd?J:e@@€¢¬,Çn\SS©¬,‚µkiiliiÌ^$66…“'Žš8q{Š··#Î uuEöêp>-Ͻ¼¼:**ÁLf »Ó‘#dööÛõÇÖvØéÓá‡ßüü9óÇo¼ŸÈ̙ۄ……BÆÆºJJr¡ÒÒÊŠŠêfß³àT£¶¶ B¨¨¨LVv{~R㜌Žç…†nãý¼ø è‚ðùù%Tªñck»ª³]k1W¯>©©©35Õçqðº(9BÆ‹?Ó¦™¯^=]BBôÝ»/FF:!a^ FE%DF¾‹Œ|÷îÝwSSýýû¯»Œ8!”ǵ´”7nœ%..Ú·oŸ;ç³ò“çd„Ëx^W„òÿzD°6ÐØÈ ýÚ«—¸žžzÇû´/èý=ø^›2räb}}õ/_.þ¾5uu+«¡Tª`d们¬Â|–/wæ4–Ö&Hkì#| ¬ɇ §8´9¹^ׯGÕ×7hj*ïÞ½pùrgöÞÙ±²2yüx{ÔØYFø èï@'ý]vzp·½±4¾€BƒqfèT`œÜ‹€î Œ3ÀwPhí´#wà3 ¿ tc(4x:ˆ»Ðq@Ü€Žâ.tlóª`!´3lqæ@Çÿw ‡ïÀgÀ [ó»q7;»H]Ý¥²ò¾¤¤XKËÆÅ¥N˜°6?ÿÖoúÀNpð£Í›Ïgdä+*ÊZ[=sf5QŒèè––Ë™úúYY…cÇ.ÍȸÖlNÞùã ‘öÆÞ€n /ïï6620ùeþ'66eÁ‚}{ö,ÊË ½yskyyU]]÷"¦¦ú g8e0šòóKxÉÙs€Føš»ÙÙEËZdôüù®bb¶krsé\1 Kûõ›yêÔÝèè::3±Ä¸¸T55glC^~Ò‚û¤¤ÔÔœ®Zu,,lGYY„®®š‡ÇvN‰55u'®óö?þDN6ËË« 4RR‚V®t›5kG}=ykð`íØØ”‹²K4'&¦{{ï>v̯¼<ÂÛ{ü÷ïy¡Â²ìì¢Û·ÿbWÜ#Ö¾SD„ZVQVÁ^Qii¥²²\JJЬYv«WŸ@½Ÿ¶~ý™›7·––†/Z4¹E-¦¨(›”t¶ºúAYY„¤¤øÙ³÷~³ ¢AÒ²IIé‹ ÝVZz×Ìl€¯ïaœ‘#B#·k#÷,~[÷¬°°´¶¶žÓѧOßÓhËi4£´´K™™×¼¼ì?Îli/_&ki)ÿŽ“<ºŸ†ý4ˆ-%>>mèP=^²ùù¹üúùõk°««¥§çö{÷bIsþ~#\hû¸{íZÔôéVFF:""Ô-[æDE%•‘&"„šš˜3gn9ràºu\löîÝËÏÏE]]qùr熆ÆÄÄtÒl#vîœïïJVvÂèÑ>!!ÏBÁÁ¦M3·±1öövtt4C)*Ê>¼ÌÜܰW/‰–Ö‚’——^¿ÞS]]ÑÕÕòÛ·„ÐÕ«O-ÌÍ %%Å uZÚh§O‡µDNnâ… ‘YY…¿_Î iÙààÇ3fX ÔW@@`áÂIOž¼Çññq:}z5.™“ÁÖ5rÏ‚ÉDÊïDßqãü23 H•—WϘ±íèÑå¾¾ÎŠŠ²ÊÊrK—N]»v¦ŠÊ´mÛ.z¯\y,%%ÃÎnµœÜDuu—§Oß#„¦Oß*#ã($4nðà9))þþ§‚‚"ƒed#"b‰ùÙ‰Œ|G¡Ð~üÈãÅ \E¡øøÔ¡CõqŸ?gê꺋‹ÛmÞ|3H,˜œüÝÆf¥˜˜­††ëÑ, F“—W ³s§¿&ññ©Ã‡÷QSSð÷w÷ð°=yòn³@tøM8ÆÝââ QQQQ}}Ïïßó°í#5k±  TE¥7¶-++%**\XXFšˆ***‹Oûõ«–Gw)м¼tEÅ/Nüü\²³C’’ÎZXººnþøñG^^qKÿ¸5[ *ƒÁ@åækj*µ¨çÎÝߺ5Èßßýë×`??œ(\+ª $-››K z ¥å¦¥åF£-çÝahdRƒmÛÈÝ&³ÕÑ÷ׯڜºžžéÑK—ªªÊOŸ>Ž=1/¯8?¿$3³`ÿþ%ÎÎŽŽkÿüsFIÉÝ+\Î"„‚ƒ7Ðéaµµ&LyáÂÀ/AAŒŒ«eehó³SQQ=q⨾}û4ë±¢â⊜º¡a?ÖFNN/—••ŠŒÜsúôªíÛ/bCJ¸‚ß¿çÚØ¬œ3Ç¡¬,âðáe••¿rrè%%•ººjÓ¦m”––¸~}³¨¨0±}jkë?}Ê`ïOÔ—N/çÞŽŽf¸£<~Y8NCíÝ»Wmí#„öÈíÛ·Ë>uÀ-QQᘘDl#>>ÕÐP' À !¤¬,çá±½²ò•*„+¸iÓ¹?þ?s¦5BhÊ”1¡°°—ššJNNìì†ýùçLN­—””.!!Ú¯Ÿ +%7—®­Ý‡{#r²ðNÛŒ3WÐéåtzyII…««å•+O¾ÖÕ5œµ´4VP!MÄʪ©)üóÏš ö}ýšÝ¯ŸJn.=:úCRRú–-çYö±jååÕ7žíß_SOOÔÀÀàýû¯§§çÒéå—.=ÊÉ¡3ÄÕÕòúõ¨¨¨„ÚÚú  œžgÖ"--ÑÐÀ(,,mläöo!dk;ìúõ¨ÄÄôoßr.^lþ/!{‹ih(¾x‘œ—W•€ñ^i‹ ’–1Ã*((2**¡±‘ññãyóöàjƒsüåV¡¢"Ol1??œAҲÆ=ºÜÇçPZZ–‚‚Œ——®Æ/_~¾~ý —ܶ 4Û÷õô´õô´EÍ™³sÜ8l›ˆ„„h^^ .1.î Öû¬¨¨./¯Îͽ)..ŠÊÈÈ××÷,.“”«¬üõý{®‰‰îÙ³÷°ÐBÌÏ#D722òïÝ{ƒ«èï¿oZZ#„âãSY¬ŽrXØKGÇ‘?â ê訖–V*(H³ÛOýë/ïôôÜ?ÿ<éà0BP¼7…›{uâD˜  à°a³gïäÔ¤ž·¨5R(LÖEϯ覆ÇòíZKhhÌáÃ7¹‡N¬¢Üã42 ï­Ý NÝ\÷¢Áƒç\¿¾¹MÒ£»w_Ù¿ÿú¥KLMõþ,~lii}¦nÙ2gæLëgÏ,Øu°¸¸Â—½àçÏTT¦-X0Éßß=..õÍ›”+\ûô™¾³o_emí{ö,š7oiûÏ]±ÂÕÉilFFþ¹s÷¯_züxii%® öFˆ‹K%åá{¸B¡uÉu"‹‹+ììVáÇ7ÛºõNñ›CH§—=zÛÁaD{8‰«¢ÃÊòÐÈüN‹fKáþpp-ûëWmvv‘¾>ù3&„ÐÊ•ntz¹‡Ç_tz¹®®š««¥¶vŸââ l|˜B¡\¾¼qéÒÃ6ü#//=aÂÈM›f_¾üxР9І†:ØŒâiÓ,–,9¸jÕñOŸÎãòëêþÏ|®¨¨„¾á‚.©Ë—;¿zõ‘½¢’’Šìì"CÃ~¬Ü\zyyõΗ/>`d¤ºMWW­o_†……!{A …rþ¼¿ÏÁíÛ/¨uðàÒÜ\zqqÅàÁ}……©«VMß´éÜÌ™Öø>z]]çO³fí R…TUå'NwJII¶±_{#$&þC< ü>Ðßm›Z ½¿}ËquµfÌàˆˆ¬ET?­ªªqsÛ")i¯ 0Ù×÷oÜ.Î8©²l»Öˆ!##)##‰éóôê%íššÎÇdéB.DŽ»”“ä0(¹0}ÙÀÀ`l÷éÓ÷C†´Í;ý“'¯¶–”´_´h?/ùÛO¿¶ªªføð…ûö]û#Dy`–ðïþš8ªçà°¦Õwíºw®ã¼y°íÐÐm­6¸b…kYYÕ£Gqmâ?Ð6qWX˜êïïξR9QvÔÜ|È›7)¡û÷߬_ï‰ý6âãÓÆŒLj³¤¤’%H Ný”Áhb2QSSMMáèÑå¸]\YReÙv­‘ nn–OŸ&”–VVTTGGÀNˆ:µ ä \ôwBññ©ë×{ÄÆ¦””T ¶‰q2º'O†YYý>‡ÿÏ?¡¨¨Lkhh$µ\QQýõk6¶`“ŠÊ4ŸC}ûN³eõ­ƒUUÅÄl--—/Z´Ÿ»~-©¢ª.Qý÷ܹûUU5ë×{p*ÂN‹äYÀDoÙO­¬¬ ·Kl+aa*6’÷éSÆÈ‘±í={®N™òï²ü¹¹t…É99t•ióæíÑÒr·cÉ,ÏšJrq¡Ý½ûŠÓ÷Þåh3ý]oïñß¿ç=ž„íeGMMõóòJòóKRS³üü\^¾ü˜›K¯¯oÐÑQ%5(''URRÉ©:¢ú©“¬¬¤ººË˜1>·n=ÇíòÒ_#†ººâ°aú·o¿ {enn(/ÿ?‹ž³tjAÉèáp×ß­­­OIÉœut‰j"Ç«ä~ú”al¬‹š7oBjj–‚Âä)SÖÿø‘Ç]¿–ÔQU—èö½{o&OÍÊ@, ÜUЉÞâN ·Ë¥¢øø4CÃ~¬y©ººj••¿èôò þY¿ÞSJJ<>>§7ܧOoNšÇtz¹ššbKO–oiøA›õ IDATËu3DD¨k׺/]zq577ܽûŠ•• BÈÑqä‚û¸ôw)JxxàÚµ§¬­W—ëëkxyÙ±¾E¢úiß¾ÊË–ÎÈÈïÓ§wP?Bˆ}7Bª,Û®5rGIIvôèA 2¬®6Q§”\ž ú»¬à1|¸à‘#·†Õ#•щId0»w_ùçŸ5ÅÅùù%§NÝ=s†Û„ÛøøTWWKÄ&苊‹K53€26ÖMJ:[TTæâ²éäÉ»yyÅ\ôk÷칂³PQQSÕ%uÛÀÀséÒ©¬ D!^^ U)Æ4€I½•’g?µ;çãv9U—Ê®_$--¡  sóæ³ääïÁÁBññiãÇ›aGCCc&LY]]ÃIó'ÜÕi>îji)sdVSSÀ†£1||œ||œ°mâ(‡••IMÍCl[UUžÙÜ*¬½zI;æwì˜{"†’“ëõâÅ\~7·q\vÙ9rÄ·ƒkdG^^šxî¿|}Y»RRbïߟÆå!8€B\\êš538O1à !D¡PÆqþüƒ¿ÿö•’WU•ÿûïPv]qqѸ»Ûhh(Q©Bïß§9pР¾œ,WVþJKËfõw7ožÃªÑÇÇ)""öû÷Üùó'651KK«°§i4šQc#{rüóg¦_«££*%%N´ %%®¬,wöì}–ª®ŸŸ Ñ휺²²VXdÅ WvŸ9É;9Ý¿ÿº©©>»Jq||ÚªUÓ‰ÞÆÄ$±ŸZQQÙ߇²Ÿ)—/+>>uÁ‚ÿYqHGGuÍš“.¬Ã:3qq©'Žª¬ü•päÈ­gÏ‘~Y FSXØËèèƒ\ªëZ´å8së(.®05û°§wQZzR?þHOϵ·Þ‘N@‚»þ.¦/kb¢‹íbÊ¡Cõ0ÙÝË—ŸHI9 :¿¨¨LWWMBB´¾¾Ñßß!¤¨([SSÏþ—GHÈ3 W&“9tèüôô\– oyyõyFF:ââ"'N„IK1bá8LjŽé×*)9©©)`úµk˜L4t¨þr¹ÿcSÕ ~$)i¿dÉr·) kò±Îm.òÀ³fÙyxü%/?ÉÍm‹¨¨ð€š˜°©©>Î[Ü©yxØàΔS£á&Uaèêªé`CåyyÅtzù™3½{Oú믋wîlÇNxÖ¡Ë—cÏÚ8U×å="¾ÀÏïȯ_u'O®Äv;Frà ø_·ƒ{FŒXäçç‚›ŠÜU¨¯o4hNpð†aà Báá¯Î‡ôˆTTTͽys+ö ¼;zD|Â>컦¦út #áåÚI“F]¹ò„OânK›kÿþC‡êaAý;?¼ùçµMMM^^sç:vŸ ‹‚þ.t2ÐßÅ÷"ÐéåÚÚ3ÂÂvÐhFíKËÈË+–Dµ€ß¡uªºD±Û6t©Õøúþ½f͉Îö¢‡q€.C³ú»K—NÅ_±ïÚ$¼Ð U]R±Û6t©ÕÄǧ¶•à.ÐR îÐehV—%ø*##)--þZTÔ“ã=þ‘‘weå/¢Â+NÈ–ÉdÊÈ8bê³!•iïß§ùûŸbWÕ%!ÂIìöÝ»/cÇ.µÑÕu‰IdÕ2oÞž>}¦ÊÈ8^¿µpá~II{ee§œz³Gy”øMJJ;v©°°µ‰É¼„„oìë' Ä]ºÍêï~ú”1þ^Gì“‘‘?~üuuÅ  ÈØØ”mÛ.„‡ï¤ÓËq ¯D!Û¯_³Œ& „P^^qIIÅàÁÚ^,UÝ4IebqŠÝ¦¤dLž¼> À«º:ÒÓÓvþü½X-¥**òoÞ·´4öó;:nœ1¦KøöígîGA4—Tâ÷Ç<‡?—,q*(¸µ|¹³°°P¿~*mùõ<ëUÐ5hVWRR,!á +^ºtêáÃ7¿²EMM¨kK²½r剱±®€€B(>>mð`m*UèÝ»/,U]N2±8HÅn7m:çããdccŠrv¶Øº5ý'ë»eË„‚‚̼y0ù#aaª’’,÷£|ciêë«KHˆr9ZTTÆ‹Äoiieyyµ†Æ¿ÒñïÞ}ñõÆÝy ý€ç»t Ø{œDâãSutTéôrìS]]Ëd2gÏÞ9vì«W7½ŸöêÕG–Âkmm}n.}ݺӘ¨íÙ³÷ëê^¾ü¸ÿu„PyyÕׯÙß¿çnÝù«4==GYY®±‘A4òåËOR—œœÆ}Ê‘‘TQ‘GÅÅ}Á¹e‰æ~þœéé¹%ñËš3…õ×%%ÅDE…o܈þþ=w×®Ë8Qz ƒ¸ @€ýÝ… ÷+(LÆ>§NÝ 8[QQ8OYYnòäÑÜ *¼êé©…l—,q:t(dÜ8¿¦&&…‚ÔB±Tu JIeb‰ÅnÍÍ ÏžýsïÞkvÿ¹G]]‘]”7>>•õä5>>ÍÔTŸûQ¢h.©Ä/•*´zõô+Žöïï•”ô]\\&Uu" ‹ è"à€{нý]€ßÿ…{~ú»Ð©@—ë!¸ÝèïÐù`—Õ€n Ä]:öˆ =ˆ»tqž Ä]:ˆ¸@ÏÞß £ P…‚˜Ìv ºÁÁtuÝ©T+UUg/¯À††Fbž¬¬B--7.F²³‹0ù^aaë~ýfþùçÉêêZN™/\ˆTTœ",lýêÕG^<üøñ‡¼ü$^Ü@ýúUëçwDEe•j¥«ëþçŸ'™mÚn,g8±fÍ {ûÕ¤ÍÈ ZZn/>dí~ÿž+*jsçÎK …†ËÉKkàhÖùNl½¸¸ÔÞ½'q|„¸ @ûÓ·±‘ÉÞ‘›²`Á¾={åå…Þ¼¹µ¼¼ª®®˜ÁhÂÖxâVXxûÌ™ÕÏž}°·_Í`4óÔÖÖ/\¸ÿܹµ))AØȼӬL&sêÔ±±);KJîž;·öãǤn´ññi—.=º|y#•ÚÊaQggZhh k÷æÍS++ví ¿ÞéÜÖ35Õß±cîܹ{8e€¸ @{Òv}Üìì" ‹eœŽÆÄ$ª?eÊyyi3³·oo—”C…†ÆèéyHK;ZXøæäÐ'LX[W×€ r©KD„*##iiiüäÉŒŒüK—MÑh¾55uîîÛæÍÛSYùkÀ/II{g瀪ªšèè::31kqq©jjÎìö›uãÑ£¸çÏ“ÃÂvëJI‰38"b§àùó44\ÅÄlÖäæÒ1ã “—.=$%å ®î‚ úVUÕ¸¹m‘”´WP˜ìëû7BˆXÜ©!„¶m Z³f¦¿„ºq#ZW×]DÄfÔ¨%ÄžœÏ¡Aƒfã]\h‘‘ïX£7o>sq¡edä[[¯ÀÜVVvÚ½ûŠœÜľ}§³Zƒ´Ý KqÍËå»ã‡Ö›7oBqqù£Gq¤îAÜ }hçQeƒkÇÆ¦\¼øý¦œ””¾hÑÐÐm¥¥wÍÌøúß)"B-+‹(+‹àŬ„„¨³³ÅýûoIM!„èô°¨¨ƒŠŠ²IIg««”•EHJŠŸ={»Ùf݈Žþ@£)(Ȱ'¾zõqÕªcaa;ÊÊ"tuÕ<<¶c饥•ÊÊr))A³fÙ­^}!tøðÍââòÂÂÛ gœ rj¥òòêÞ²Ä!Ó½½w;æW^áí=þû÷<œ·#GÄ%ŽÑ_QQæþý7¡ì좾Mž<†=CaaYvvÑíÛ%&þý5ZÚ¼Ûz!?þáB$©{whk:6âb88ŒØ¹s¾¿ÿ)YÙ £Gû„„|ø¦¯¯Ñ»w¯ÿ2<š6ÍÜÆÆTTTØÛÛÑÑÑ ç˜ÓéÓ«‰gÁj ±±1•––`?ª¨(søð2ssÃ^½$ˆeq´¨y;·õ°tÍè͛ϤîÁ|fÚŽv˜«\\\¡ª: ³ÚÐЈéïê¼ys—ÓÏÏÅÏÏåóçÌ‹ººnNJ:››K}ûöóßq*?¿DII–»©sçîoÝtì˜ß¨Qƒvï¾òû ''•šš…K,((2DÛ–••.,,cÏ "Be0!§ÄÄoêê.j­\鯽 ñÔ J45•XòòŠutT[q..4[ÛUuu 7oÆx{o…Œ–6o綆¦¦2§‡ÖÐß -h·>nïÞ½jkÕÖ>JM½¨­ÝÛ&]ýûkîØ1OGG51ñ›‚‚ÌܹŽ×22®ef^+- §´p=¬ššº›7ŸMœ8ŠhŠ=[tô‡9sœœÆ*)ÉbU 4408™mÖ ssÃè踩ÔJJ²¬™e¥¥•µµõŠŠ2d¥‘¬¬THÈÖ¢¢;+WºMŸ¾U\\„KAâ©á¦þ*(ÈüüÙšüˆýee%ƒƒ½{÷7ÈÌ{k¶±y¹Ó¹­×¬{lý]ÂÜn€æá÷qƒED¨“'‘––xðàmN}̘!zzꎎkÇ7;vÈ—/? Ù³gQC£°°TN®— 'kuu FSrò÷uëNkk«¸¹Yêè¨âLÎgå×ÐP|ö,1/¯øË—Ÿ7nD»¸ÐúõSÉÍ¥GG““Ú²å<ξ´´w7ìì† ¨åì°{÷B55…ääïÁk׺;9m˜;×qÀ­€€³––Æ 2™™Äâ;w^61ѵ¶ª¦¦@¡ 7·q3flÅ,((Å2Ϙa…;5ww›ìì"–5WWK›•6#G¼ví©‚‚Ìøñÿ3Ô|äÈ­ÄÄoœ†šW¯>aee‚dæÔ¤íFl^N¦ø¡õ°vÈÌÌWV–#u￸ÛÙ¿ õðÇ]è‰ðÓµgnn¸m[ÐÖ­AÕÕµh…†nÓÔTÒÔT:zt¹Ï¡´´,//;ÉU«Ü45Ý$$Déô0NÖäå'Q©Bêꊮ®´€/aà p¦Øóûù¹DE%¨ª:kk÷<¸/BHEE~óæÙ&¬UR’sw·NHøÊž¿Y7(JxxàÚµ§¬­W—ëëkxyÙYXîÚµ`Ò¤uEEe4šÑ¥Kë9ùß·¯ò²e‡32òûôéäog7ŒKAâ©餤d””T`ó™ÍÌ=ºÜÛ{wVVá!ý.\X‡«îË—Ÿ¯_"õÄÅ…¶oß5î‘×Äv#6/w:·õ°ô¨¨„#ú“»×¶¯Çÿ‹·À7t8íqA?˜2e½¹¹áŠ®íH—¤©©iР9‡-µ±1Å=¢. ?u2€Gw¹üŠ‹+ììVáÇ7Ûºõè‘M›f;8¬ñò²gÍjæCø¶õΞ½/##ItBÐßíJ@è\:&âB—oøóÏ“>| lõ’U=“øø4{ûÕ11‡û÷×$9L¡AÜå{ ÜNGöq!îÝgækºË€Ð…‹Úˆ»ütp~".´wù·ŸÚX¯ª³ÁVùa-ô7; éŒu•Û††ÆíÛ/²$xgÏÞ‰-·‹$x‰Î´Ž«°û›@·ó€^À?t…«±±‘QPPªª*Ï)ôi……¥ÁÁôôÔ³² ƒƒ¿x‘L\X˜w ^AA„„¯þþ§ìí?FGÄwT0 Þ7¶èë«sqŒ%xËË«#"vêè¨&&¦3M\–Ùê`0Ù–lQÇ+ìòsãpú»®ƒ K×¹¹ëï>~ý!"b×ðáýed$ÖÞ¹sþìÙö$x[+"Ë(ìriœ–q·£€ñd€ßè:—ž>}om=”¸ÈHð¶ZD–PØåÒ8-ƙ۟®0‚ô,ºã5YZZ‰-&Œš2e}tô„P`àüŒŒ|L!´pá$“y­0®¢"Ÿ”ô¥´ÊÉÔéÓáçÎÝÿòågUU‚‚ô!ý~猸‹È"„¶l™#'7‘]D!äêjyâD"ˆÈúúþMZ£ÙSóñq"ujvv¶`)ìf±éïa »¡ŒŒüfÏ×z]¨qZ ÄÝvæ'|HŒ¸<êïöî-ûïÒü'O®ª©©óò ¬«k Þß‘m–ž¬°Ûj î¶5nþ¤ F\ L!„=8üöí2i6 Ã]».ýš­««¦¤$‹FÿÉ£îÙ³ˆ•“Tú ˜o`àüwï¾àLÑéå¬m–F,úOPö÷%xOž¼[]]+!!ÊJ$‘%=#LD¶±‘q÷î«éÓ·º»[ ²Ä숭Ä#ì »aa;8eã]añÐ2¨‹4'àùnÛoþ¤{=Ç儵õPKKã‰ýŸ?Oªªªùñ#¯¨¨!4c†UPPdTTBc#ããÇóæía©½66r Š¡ºº†òòê/’íìVc¼DSìù54_¼HÎË+ŽŠJ¸q#!Ä’’MJJç"ÁËÉ –ˆlrò÷ÒÒʘ˜D‡5S§š_¹ò$!ák]]K –´øÎ—>|' @a‰Èr)ÈýÔBGŽÜ"&b´Ha·±‘AÚ,ÄÖã_5NKþîo\€oé²}ÜV@¡PîÜÙ¾iÓ9í99EŠŠ²ŽŽf“&ÒÖV ÞV‹È²…].ÓR@¡µ@¸ø™®qAèÞ€.B‹p ð9]7âv8|¢ÞÊ'nð'ݲq ¿Ë3p;øœîq‰BèÞ€þnó@àºGÄÅàa.+ti`œ™n.AwЏÝé\x›pÞÓè™g q—„îw#º%p¡vzjøé±@Üýèà]ˆ¸Ý º=w!Ü]ˆ¸Ðõé©qÂ-е€ˆÛ-În¤çÅ]¸] ¸b»+t{*=&îBèr@ÄíÆ@ÐE1™=³º{Ü…p tE â@÷¥ûÆ]¸s]¸n{=²“°èvq:¸@"n‚n‡ÿânë.J·œ€U÷ZGG^Hq{t¾‹»­¸(ážÕ,°Ê|K¡Ð:ª"¸z ÇÁOq·EA:¸@—"n:»B!Îv …P(ÿ~˜Ì?@Ëildëë{ [KK;:8¬yùòcll …B30ðl÷î½F¡ÐXŸèè¼—ÍÏ/¡Ph’’öíáX'À~=ºÀðM—ûE ½Û6ÅÓsûÕ«OB£F b2™‘‘ï””ä.œÔ~5뺹»ví)BhÉ'UUùö«‹>n‚.Àôw¹\”лmk^¼HÆ‚îéÓ«_¾<òêÕѨ¨ƒòòÒìyrrè&&ó$%í©T+mí;v\B555mÞ|^]ÝEHhœššóŽ—ˆ)œ*µ²2Y»v&¶}䈯®®Ö½ÖÑ™¹xñII{55ç'OÞ“Öbf¶!T]]K¡ÐddI}ãd !“H£ùŠ‹ÛIHØ»”N/ŠJ03[$*j#/?ÉÏïHc#£]šèãð|Ðß% ºÐÁm7"#ß"„Ôš;×K±°0´°0ŒMa婬ü…Z´h2…B¹zõéúõgŒuÓÓs·l9od¤³xñ”Ì̂ϟ3»ƒKi©3éé¹ÇßAUW×®^}üýûÓD› Nö÷?%,Lݸq–¨¨0©o²²R¤ÖRR2lmWÕ×7º¹Y**Ê>~ÿòåG7·Í½{KïÛ·øúõèƒCTUV­rkƒ–%ú¸tvÿ¥³ã.pÛþÐéå! .y 4üýÝ INþŹϟ3¿}ËAÕÔÔIJŠ­Z妣£ºxñ\JKQQ‘?õæÍç)SÖ§§ç"„RR2p6óóKüýOQ©‚6üûì™èÛ¨QƒH­9QW×àéi{áÂ:¬ìŠGëê||œ¼¼ìuuÕbboß~ÎkÜmÅÔ?¸’{8t¹Ó#—Šäqfãɇœ\/„Pnn1—<.DººnŽM>Ü‹ÐMMÌéÓÇ SSS³–-;¬§ç±cÇ%bJK‘’SV–36ÖA1 „P³6I}ãd-;»!db¢Ç*ž•UˆZ·î´””ƒÝj„PaaO¾ò~w€Qe£çE€:5îb¸Iu,VV&¡ØØ”Þb)±±)kמbÏs÷î+„жmÞí377ÄÍÍ ?~<·qã,cc]&“¹{÷bJë\øÿëh“B¡ „Œ&.¾q²Ö§Oo„PRR:.eñâ)ÏŸÿ}®\ hÛ$ÀÅ @sñËzFæF¿!Žg2q⨻w_9:®=z“É|õê“§§-{%%9„PiieEE5ë©íÂ…ûkjê 4LMõ¾öï¯ILáTé“'ïOŸǶ}|ùúN㔓hSVVRD„Z[[ï娷oRß8ñÇãOœ;þAEEµººbtô‡õë=Oœ»xñá¯_µ²²R™™L&34t[3­ÖlÇF•ÐÙ8 „POZϨÃÖ!âoBB¶_¼øðõëOââ¢ãǘ?{÷7oRöï¿ùNHHK6L÷î«—/?ur{èÐÒ‡ßáR8Õ˜ð{‰!tôè-gg QQaÒœÄZ„…©{÷.Þº5èÒ¥Gýú©DE$úÆ CÃ~‘‘{6nüçÞ½7L&säÈ––Æ‘‘{¶l9ãFtmm½ŽŽêâÅSši/^^rƒ;,À]€3&êaq·§ý(”Žü~IßÉ °ývp× §{(D\€º-¢ç5Wk¥PhcÆøpÊÀËCÝm"¡üü*ÕŠø±µ]ÕÙ®ýœÞsƒç¸´ŠÎ~èFÈËK¿{w’˜Þ«—xÇ;Ó^@àNÏë½-…¼¿[[[ïá±½¶¶!téÒ#ìÉ\pð#uu ûÿaå$]<ˆ—†py šM¡ÐÂÂ^"„®^}J¡Ð¬­W`‹©ª:O˜°VTÔFCÃõáÃwX½¤Kyxl¯¨¨FmÛváÝ»/íØlBB‚¦¦úÄžžzg»ÖZØï¡ÐÇš‚.À$ýÝÜ\ú”)>}Ê8sfõš5'öì¹²5!áë¬YL&sÊ”1……¥¬Ì¤‹ñ²À.“É\³æÄÑ“&¾uë9BhÖ,;¬Š’’ M!!Á;w^Κµ#;;$55ËÁa qÉ¡gÏ>Œ±(,lGEEµ¹ù²Ó§W{xØtD+ò90›¬u°î¡ÐÇx‚.Àø¸›™Y0räbqq‘ׯΜ¹-""öøñÓ¦™/_~¤©©ÉÝÝæÒ¥õ¼upXƒå']ØhölûfÂåÁÖ$ {UQQ}ÿþ ÑiÓ,’“¿#„45•öî]Ä`4ÉÉM,((MMÍúçŸÒ%‡ž=;4mZÀˆ‹>Ü[]]ëé¹=/¯xõêéÖš|JÏ™7×V°þ©@Ä ­Á33™Ì¦¦&*UHX˜Ê`4 ˆ‹‹ „²³ BC‡êáòsY<¨Ey”•åìì†WTT¯Ys²²ò×Ô©æ¢ìää¤BUU5œ–b¹A¦×@ó°¿PÎRŸN—tv[ ¶TdOwµ´”ß½;)&&2|øÂ«WæÍ›àåxçÎKlÝù’’ \~ÒŃxY`—!4{¶=BèÔ©»!//ü<ç’’Šœ:BH]]Ó’CcÇ.--­|óæxHȳ#GnýóÏ÷ßk §ÂZ²”ýà À쀠 ´’ç»êêŠ/^üíåH¡PZ:dˆö¯_µöö#Μ‰8räVSóùó$VfÒŃxY`—góæÙ“&–“ëURR¡®®hiiĪ"7·xÉ¿; IDAT’ƒÏŸ'544Z[UQ‘Ç!".94l˜Á©S«de¥DD¨OŸ=zPû5Ð{+@‚.ÐBÈç3‹‹‹^¿¾[QÈÛÛqúôqS§Ž]ºtj]]ÃùóØEËýýÝMMõ÷ï¿>v첪ª,[`HAAæÒ¥G—/?æ%BHD„jf6!äîn;̂`LLâ—/?­¬L0U##ÈÈ=&&º7nD>|óÓ§4šBèúõÍX¿|óæÙt>„Ö«*/¯îÓgjMM]JJ¶ÒollÊÈ‘‹õõÕ¿|¹Ø6uÀzU/ôÀëàb×:»mBkF>Z½ïêÕ'55u¦¦ú\–×àzX´Ú >êïv=°ZGO»N^À­£I[ÑÃÖ‰ìôœÿUm¬4@»ÁGãÌ].jÐwŽ?ÒÕu§R­TU½¼‰y²² µ´Ü¸ÉÎ.¢Ph MXغ_¿™þy²ºº–4g³¦€/ ]kw<q·çÒØÈÀ–"!%66eÁ‚}{ö,ÊË ½yskyyU]]1ƒÑ”Ÿ_Òl]tzXaáí3gV?{öÁÞ~5ûz)-5YqA".ÐrÈãnc##00X_ßSXØZZÚÑÁaÍË—1u Ïöðëá>W¯>mºŒìì" ‹eœŽÆÄ$ª?eÊyyi3³·oo—”C…†ÆèéyHK;ZXøæäÐ'LX[W× #ã(#ãÈ¥.ªŒŒ¤¥¥ñ“'22ò/]zXXX:`€—ˆˆ¤¤½³s@UU »)âѶ?h5¬ÕÊ â¶ =l©Hò¸ëé¹}ݺÓiiYÆ ¨ùîôéðvõÃÎn˜Ý0ì "Il[èÖŽM¹xñ!{ÌKJJ_´è@hè¶ÒÒ»ff|}‡‡ï¡–•E”•EðbVBBÔÙÙâþý·ŠŠ²IIg««”•EHJŠŸ={Ýñh»(ð Xá".Ð*Hâî‹ÉXGóôéÕ/_yõêhTÔAyyiö<¤’ºMMM›7ŸWWw§¦æ¼cÇ%b '?<ØóàÁžÅ‹§ „ÔÂv—,9ðû¢¼@ëpp±sç|ÿS²²Fö y† ~>jÔ9¹‰.DbZìp? ´=¤«OÇt!îNw†?ϼGù!4p ÖܹÿŽZXZXÆÆ¦°òJꦧçnÙrÞÈHgñâ)™™Ÿ?g;v—Ò"ç¼¼ì_”·UÍÒ).®PU†b2QCC£¨¨ BÈÐPç͛㸜~~.~~.Ÿ?g^¼øÐÕusRÒÙÜ\zxøëÛ·Ÿ£ßxÁ5?¿DIIöܹû[·;æ7jÔ Ý»¯à¬q? ´ðÊÐ:àÕ»–@ÒߥÓËB˜`'0ÙÝׯ?Ø61è²èß_sÇŽy::ª‰‰ßdæÎuÌȸ–‘q-3óZii8¥…ÿskjênÞ|6qâ¨èèsæ889UR’ÅŒ°›"å;à?>­‚$îÊÉõBåæs)F*©;}ú8aajjjÖ²e‡õôª_W×P^^ýâE²Ýjmm77K Å/’óòŠ£¢n܈F±›"åX£jðx¯c!ìIš“}(§íVТâùù%¬ù¡ö††Þ{÷^#ÆÏž_R¯ tKH⮕• B(66åÁƒ·XJllÊÚµ§ØóJêš›~üxnãÆYÆÆºL&s÷î+Ä”–ú÷û¢¼@ë077|øðÝСóúô™ºgÏÕÐÐmššJÆ=ºÜÇ瘘­­í*yyiÉU«Ü45Ý”•¸X“—Ÿ¤ 0ÙË+pôèA;üü\šššTUçÍÛ3xp_„»)âÑNn!âv8?Ù³M°´4:T/%%cõêãu¶;_@ò|wÜ8“‰GݽûÊÑqíèу˜Læ«WŸ<=mÙóJê.\¸¿¦¦ÎÀ@ÃÔT?!ákÿþšÄ”–ú÷û¢¼-­±ç ¥¥üíÛeNGGôàÁbº‹ ÍÅ…Æž²kׂ]»p²£¦¦À${j('×ëÅ‹#¸DvSÄ£k$m§Â>Ù›wòìYâÝ»¯rrè'ú§¥eÕÕ5¨«+Îë¸nwSõõK—:þ¼¼tP¿¹¹!'#11‰gß¾ýB¡PLLtÿüs&ËÈÒ¥‡Ž¹ejªÿâÅ*—ê._Þ¨¬,÷òåGKËåW¯>uw·16Ö%Vgf¶!T]]K¡Ð¤¥%>} jéy]òõ™CB¶_¼øðõëOââ¢ãǘ?{÷7oRöï¿ùNHHK6L÷î«—/?ur{èÐÒ‡ßáRZê&Ê{ï^,©(ojjN”wË–ó7nD×ÖÖëè¨b³£Ž¡¸¸ÂÎn.qüx³­[ÿè~ˆ¸ü§Éž_¾üD„ ž˜7'22ò?ÎÔÐPJIÉðò üþý é,QMM%[ÛUõõnn–ŠŠ²Ç—•UaîßsäÈ-YY©7¶pº,F4~üˆ;w^†…½ÔÑQ%V·pádÿSÂÂÔg‰Š “ºäà0¢õ-ðäqWX˜ºiÓìM›fãÒYUUùwïN z{;âvq)¤O(XLõñqòñùŸáÊòòꨨÄ6“CQQ&9ùÎŽ¥¥±¥¥1éíMïÞ½ââN5Ÿ¯«ÀZ™ˆ8y "q‡Ãi²'6ÁóСääX¬úü9sÔ¨A\Léé©=~¼ÿׯZ…)ùiiÙ¤F=Š««kðô´ÅþÖ#„°:ÊË«çÌÙI¡P‚‚üµ´”y? %„Paa)iu³gÛûûŸ¢R7lø÷ù11ÄÝnC‡êåç—ôé3•˜neeòøñ~Ò" Ê t¬ K\Œ—˜hO8Mö¼p!ÒË+PPPÀÒÒ8'‡þùsfSOß…¸¸¨¼¼ôÏŸµeeU¤F°×4LLôp±¥L””d[t ØÄOeåÞ¼øÜêóºº>³¼¼ô»w'‰ŸcÇü8Y°`“ÍÞ·63ÀdFùr±C\z6Ø*\ eM­b}` v†ÓdOÒ ž¼PRREPUUyR#Ø<ͤ¤t\A55…5kf455-X°Ëüdoß~¾wï BÈÙÙ‚´:ìe9–ÁVŸW¦'-)„Pǽò,„iÇÔmq5|bÐ'nS8Mö$àÉœú¢EûŸ?Oª¯o°²2QSS 5‚ÍÓ<þAEEµººbtô‡•+ÝB¢7κtéчß¾éç罺™3·555½zõ©¡¡qÎkë¡·o¿ V'++)"B­­­÷ò ìÛ·O+Î èBÁíš§ÙÐK̃ØiZédOMMeâO‹¾x‘œ––mee‚­äC:KÔа_däžÿ¹wï “É9r kº–¤¤ØîÝ <<¶œuq¡©©)p©.**A\\´Í9s|}§qªNX˜ºwïâ­[ƒ.]zÔ¯ŸJTÔÁ–žÐ… 0á÷ß½¡PZ½ø_CCãîÝWΟ‘‘¯¨(kccºaƒ'6“¬¬Â±c—fd\ãd';»H]Ý!D¥ ©«+:;[xá–@áÑTA¡‘ÇEÒǽ­¬‚ÿ:Ä¿q©ôX¸OíApúÉ´ÌHKþÚve:t^ÀW462 JUUå9e˜6- °°48xƒžžzVVapðã/’‰q—w ^AA„„¯þþ§ìí?FGÄß›ø]‚»)´É݆¦»>­˜( ú»ÝΘŒŒ|k뜖Îxü8~êÔ?~\íÝ»îPhhÌÚµ§ JŒt._Þhg·êÓ§ ii „© Öß­¬¼)øVW×xþõ—·ƒÃmyzz.•*ho?üüy3³E,Sii—pG±âA³Þ;æyGbèï¶ÆFƇ߈é½z‰ëé©w¼? ôw[ôwrž>}om=”t1 Þ'Oö éï“à50ðäQ±IðzyÙ'%ÅçÎ݃Ið²›Â]¶lZ[œY[Ð1wxHÌß ššêw¶@×â.@Nii%öÒ$BhÊ”õÑÑBó32ò1 ^„ÐÂ…“LLæµÂ¸ŠŠ|RÒw„ÐéÓáçÎÝÿòågUU‚‚4.÷£=‘ö~“ÔÜ ý¸ÛãàQ·wo騨OØöÉ“«jjê¼¼ëê@‚—hó‡Ä0Î ´øÇÖ îö80ý]ÔÜó] Ã]».ýš­««†-Í#&&ŒÂ$x÷ìYÄÊ™™YÐ"0 ÞÀÀù¼ÅDvÑëJðâÒnÀt-à{zØdw€g¬­‡ZZOœèÿüyRUUÍyEEå!àíbÀêZÀg@ ‡B¡Ü¹³}Ó¦sÛsrŠeÍ&M¥­­‚Ið¦¥e)(ÈxyÙ±ts%$Déô0Nåå'aïﺺÒ¼0 Þ¨¨UUgmí>8 ^ Ñ´´K¸£@ÛÀKŸ:ž–®NÓe÷ˆº;ðrH+h“—"ºp©­¦­~2=#îBh3º›/@;qh3º›/@;ãÌÝxt×:zàï.àw€qfžþnw‡Ÿ/âžñëJÀó] uÀû»-â.д¡°@—Þß:ì…QìER »pìØm …Œí>}ú~È?BgÎD,X°K 8;þ^Þmúø¢Ph?þhso s¸ tq»2……¥µµõœŽÆÇ§éêªED¼fíb‚/_&ki)³MLôx¬îǼ»w_éë«gdäÿžãÀw@ÜÚˆ¸]Ÿqãü¸,Ÿº~½GllJIIB(>>mèP=ÿSAA‘Á22ޱññ©™™ùηcõŒ9ñíÝy\LëÿðÏ´N5Q)í!mܨ¦¾¸Ü%²k³%u­²_!ËÍr]‰×zEDB)Ü"é–´)ÓF)mTÒ6íÓüþ8÷Îon›R©f>ï×ü1sæ<Ïùœ3gÎgžç}/&†îããÜ× ½u&žßE¨]˜qÑ·º?ÊÅåj‹‰Gެž>ýd2››X,VBÂÛ'|ýýõZŒõlÌ˕B{÷Â… ;Ï1uÚÚ#Ž]cj:®¯#B€íÝ®Á¼‹º3.ë`Qw`Þí4ìgFÆßž†nÇpÿG=ó.êlæ"^†û?êQ˜wQ‡ðˆƒxîÿ¨`ÞEíÀ#âe¸ÿ÷ø«HÌ»¨-ܾß#ÔÜÿQo¼‹þ æ#^†û?ê}˜wÑ¿ðˆƒxîÿè{Á¼‹ðˆƒxv,£ïó.oÃŒ‹x~Ðw‡y—‡áo|ÄË0ã¢>‚y—'áñ2ÜÿQŸÂ¼Ëcðˆƒxv󠾆y—g`ÆE<¿¨À¼;ÀuæÇ;nïêO0ïdLºx¸A¼ ¿·ÿU$æ]î…¿ñïê—0ïXüÄà âqø@ýæÝ©½¤‹‡Äãð+€ú=Ì»Ü7qõIAÄ50ï@­.x¸A<w¢óÄãð+€Ì» »i‹‡„°§ @˜wâ(ƒ!ü  óî@ƒ?ðÃŒ‹8Þ˻ėv@ˆ«€GIÔ}˜qWོ ¬ð¾Ž€Çh}ø°§‡§põ_EòdÞE ØÌEÜ…¯¯è{®®¾$ýOê|ÙËH$…bÚ{áu¼hâ! `¤¦¶ôôé{ßm¹}²ÊˆçHÿ4z0é".‚yôôÔ-2"ž;8˜)*J÷m<]5uªÞر#³² 7mò Œêëpê!˜q—¼ ÆÆÔ]»–Ï==ÕÕ•bcSI$ššÚÒõëOR(¦JJ–OŸ&@ssó^ÊÊVFJJ–GŽ\Ÿ0a0u$MBbvAA)•ºšB14VU]räÈuh¯6ˆˆxM£9ŠŠÎ354ÜXZZñìYâ„ ëÈdiéy[¶x651¿¿sBÂEcc*¿$§®n½cÇŠi\\FPP´®îJ!¡irrf›7{Ö×7ó(*ZΙ³‹L6QQYøøñ+¢¶Ö´¨P^Þœs•µµíH$‘ïoÝ #‘hÓ¦míñÏñv3!n„y·]YY…üqŸÁ¨+((ݱã8{öþÁƒ^ÒÒƒ]\VΙ31--×Þ~> º¸¬Ü»wyUU ¬[7Ë«¦&æž=—þúëE{µ¥¦æLŸ¾=""yþüI«VÍ.+«ŠŠz3sæÎ¼¼’'ÖÿðÈS§îœ:u§“Ñ644€¸¸(ñòÝ»WW_£.>>ÃÌlïÛ· eÜÝïlÙâIÌSVV©¥5ÌÔt\^^ñòåGšš˜tzN{°+´°˜Ì¹Ê¶¶¦àçþþ‘°|ùŒØúˆ7aÇ2â˜wÛ¥  ]Tt/ à0de@jjÔÖÖS("Û·/òöÞcgg ‚‚ü{÷Úlß¾HKKÅÉÉ:&†~þ|P~~ ¤¥å¶WÛ¥Këë—-3¹ysŸ»ûF:Ýëï¿“êë7l0³µ5ݳgD~5ΟþJ]™,((°l™ 1QEE6?ÿ“F§ç0™ÍëÖÍ?yrƒŸßðò nll€aÃd]]×ݽë2hاO_22ò._~Ø^ì ==7s®²Ít~~¾ÀÀèÊJÆ_½#[XLé±Ïñ̸ˆg`Þm—¸¸ˆœœ”žž0™LX¼ØHHH0##oÓ& eD72§k×B.<›:nœ––– 47³Ú«HÌTª»x^^1ìÞ}Q\|æŒ; ¸¸ü«q¿LIÉÖ××xôèØ˜1ªÄD!EEi>>¾¢¢Ï ª*ÆÉ@mm}YY»8??Ÿ””8TW×v»ÂK—““š1c\e%cçÎóUU5ææ“ÅÄÈ_¡ÿÀŒ‹x ^Gôœ™fòd7o®x{?~ð &1ñíï¿ß\¹r60™ÍÄ AAÑàâ²ÒÉÉÚÞÞÝØm³6yù!œœÕbÊúõ –,1&¦ˆˆ5¢¢{rrRí½«  99àÇODD¢%”•U”€²²L›c6‰Ä¹Ê`ggúèQì… A@t;#ÔYxâ=˜wáéÓ„‹Ï7lpwt´hoN{{·ÚÚz--ÍÄÄ·£F “”¤ ÖÕ5ØÚ1B^VV ¾|©ª¬d´Nº-¬X1ëܹ@/¯àÊJ†²òÐðð¤={lÎ ôö~\SS'))ž›û‰ÅbÝ»çÒµ³µqæŒÿ™355õQQ)`gg*((……ŸNEF&766M›¦¯  M„Ô"€;—pVØb•°›7o’”Ô ²²Jeå¡S§êv'ZÄC0ã"^…ý̘øÖ×7Œx~æŒ?ÑøkÓÿþ§›ºoߟ¾¾ÏÌÌ oß> $$èêº^FFâúõ'>>¡NNÖšnn· 7UW×v¼\‘!!Ç'MÒ~ôèŹs’’âS§ê…„§RÕýüÂ=<îÒéïi´î¦1*UÃßÿººâ… A……Ÿ-Ý܈·ø#"^§§06¦^»¶tuÕ¾@‹UaaÁ F€µõ´Öш·tòOL±cñ0‹×v}i`ýOd›—ññ‘º“ábcSüq½¦¦rzºw7BûGEC^Þ¼¶¶>5õê¨QÃÚ˜ƒDÃ#,OèÌÅ?ØÌEÇ¥—“a?s¿öñcq½l ÆÆÔÐP·ïO›nÝzZ[[o` ÙvÒEˆ€!Àön?×ÔÄLJz×zú A¢Êß?žo„í]^ÐAÓ3.ú6\ÚÞż‹zæ]^ÐÞ!’Kè{àÒ‡'ó.úþxm7ã5m±™‹º©õ~Å™˜'Ïïb{÷;Ãûïò̸èpENí ¼ê!Ô=-—xú6Ľî;À-‰™'Û»¡Þ€Í\„:ó.B¨ˆ&f\Ô#ˆ}‰Ûèa?3B¨{°cõ ¯ö6|ØÞE¨ë¸ý¸Ðe<¾Að7ê Ì»}8\¿Ç±{›ÙO¸¨“x4ïâ÷!„Páɼ‹-•ï èt(,,ÁÅåZ\\F]]ƒœœ”¾¾Æ;¿ ð·9sÏÞÓ¢qþ÷8‰D2d¥å”S§6 öÒƒƒ_Μ¹sÒ$íçÏ={i¨Çp¶q¹«± <šwê7^¼H›>};??ÿÌ™ãDD„KJÊcbR›š˜íå]î3wîĺº†°°„sçeöîµéëˆPÿÀ½¬p<3B})44ŽÉlVP²eËB//§ÐP·OŸüÉd¡ÐÐx‰f`°ÒÓ?H4%%Kv©††¦ÝÅÅgޱ8"â5ÄÆ¦’H4EEË9sv‘É&** ?~Åž®®n½cÇŠi\\FPP´®îJ!¡irrf›7{Ö×7À™3þòòæââ3úi‰Dûé§ ­ËÞ¿E¥®¦PLUU—9r=šÚR{{711SMM›×¯³Ö®=!*:CIÉ233ï«[àÏ?yüØÕÆf:¼yóž˜øìYâ„ ëÈdiéy[¶xwÃŒˆxM£9ŠŠÎ354ÜXZZÑz]Z¯ïO”•­ÄÄL/÷ô§‡¾ îjì¶wê[::j“ó‘Fs65wèÐÊÑ£‡w\*'çcZZ®ŠŠljjŽ­íÑìì›Äô²²J-­aü÷ïG-_~$?ÿ1ýÝ»WW_ˆÏpp8%,,äà° ""ÙÝýNCCã’%Æ6¸“H$33Ã’’òËb—­ªª€uëæ“H¤[·Âö칤§§.))YY…YY™™7i’ƒQõ÷<=¿ºJJÊ““³@[{Ðé93gî2dð‰ëoß?uꎢ¢Ì¬Yã§OßÞÐдhÑÔ¡C%CCãŸ>·¶>Ôb]–/ŸÁsbâ[{{7‹µ`ÁOÅÅ_:÷™ð’Ñ ìçAvýgæ]„úÒœ9?ž:µÁÕÕ7?¿¤¶¶Þß?2<<)3ózÇ¥44”BCÝjjêddää|ÌÌÌ'¦&ë꺎Él–’šûéÓ—ŒŒš›**²ÑÑgäå¥6oöd2›×­›ïêº.;»päÈ¥^^ÁLf3ØÚθreq”sYì²|||‚îîwRRÞ98--wâDmPPNL¼èíýxûö?ebcÏž<éçâríýû¢¯n™ùÄ“Ÿ~ãèh—/?¬¯oܰÁÌÖÖT]])"âu@@daai}}£Íôk×vóoÚäÑz]–,1æŒyëÖ³ÍÍÍÖÖ&ׯïi½jÇ»tÏ7 ^Á&>ꈼIDAT~f„ú˜££e^žîåâ²’D"}ùR–Й‚¢¢diéÁP^^Í9ŸŸOJJª«k‰)""BŠŠÒ|||EEŸ@UU† “€ÚÚúÔÔø·åÝ»ìµk! ˆM7NKKKš›ÿù¥/..2t¨ä¨QÃ@R’"))N>|øP\^^ýî]A›eËÊ*?~,EEi"-±§”€²²Ì‡ÅœE¤ 'ç#|øð DD„ÕÔŸ?O!²]‚‚¢ÀÅe¥““µ½½[ZZn‹øøþÿw<©Ó}ƒgÏn=zøÁƒ^kמ00ÐTSS”—ë×/ ¯D×®…@rrVÇëBüà`#ºÁËÊ*; c2›ååÍKJÊoÝÚ·h‘¼yó~̘ŸÉd!gssgEEiöÉ 6bhº˜¹º:¸«Kìäøü¦&æñã·¼¼‚ß¿/ž8ñ‡½{—Oš¤ÝÕÅõT<ÝÄ{y—ëNÑ£­¼¼ÚÂbŸ––ʘ1ªii¹¥¥ªª &&úÅÅåPRR.)9§u©‚‚ÒuëÜ"#“©JJ2DÞ-,üìàp*22¹±±iÚ4}éy×ÖvÆ™3þgÎÔÔÔGE¥€©‘ÕË+ØÃãnZZnRÒ»öB•••€/_ª*+­“nw8;/ ‹Šz³pᘘ³+VÌ:w.ÐÛûqMM¤¤xnî'‹µ¿Ý¹s^^Á•• eå¡ááINNÖ­×EPð?Ç4SÓñ—.=ôôôonfEF&÷`Ì\‰ŸŸÏÜ|òùó÷ïGy×ß?LMÇ©«+-Zd4dÈ > ÌÆæð­[a0q¢6‹Å y%++Õy÷ûÀ~f„ú’•mÖ¬ ¥¥÷îE|üXfmmò÷ßäáÃ圗‹Š’GŒÿí·5-J‰Š’Ÿ?OÉÌÌ76¦z{ïaOàˆxžþÁؘÊ>ʉJÕð÷?¤®®xáBPaágGGK77 ‹É;v,#¿z•®««ÂÂB­Ë:9Yhº¹Ý64ÜÄîÁîüü|7n8,–˜øvóæÓººj!!Ç©Tu?¿p»tú{MWGgdHÈñI“´=zqî\ ¤¤¸±±~ëuiQ³¹¹áÆæõõ^^ÁŠŠÒ=3·²²¢À£G±Mðœ˜X]]ëëöôi<´èN\‡Í`Ô‘H4 ‰Ùí Å/((m=¾3ž?O!’îÅ‹;¢¢<££Ï<{vŠ8ÃÒÞ€v5µ¥ëן¤PL•”,Ÿ>M€æææ¼”•­Œ””,‰¥·9¶ŸS›ãê»ÄÂöB]E"õ·Ñ(ÝìSV¶26Öä y•—W|òä†Í›-¿^¬ÚÃ]ã™ûЀúþMŸ>¶wBõ1Îû1s26¦††º}ÿxzæ]„B}Œ§†¾cÞE!ÔÇxjè;æ]„âaÜ8.¬ŸÃ¼‹B<¬ßŒ«¾éW æ]„¾ ¶Bßó.Bß[ ˆ€¿ÀPõãë¢B!®ƒy¡ïêìÙ‰Æ~ðñM­ªªéÁú/]z¸ví‰f¨®®7ÎþÄ _âeJJ¶™Ù^ ‰ÙÂÂ&kîßêÁ`¾™£ãé;Ïurf£nófOYY3AAcMM›sç¡ýí°mÛÙyóÚ¸3qŽó;vþÅê)˜wêaÅÅ_êêÚ{7>>sãFó/_°_ýÿÛ.‰ŠJ>\®ƒ–-;4oÞ¤mÛ@XX¶™FÓÍ̼ž›ëkkkÚ¥?Îí=ññ¿ªÄ¹  $2Ò#/ÏoË«ââ/ÐþvظÑü?¶v)˜­[–—W?y×¥Rµó.B=ÌÈhKnî§öÞÏøñÇ$$(Äcð`±bÈd“²²Jðò ÖÕ]YUU“šš3cÆ)©¹ÊÊVaa ’’mb²MDdºŠÊB?¿p‹%!1›¸«(((X$$d:9]¸z5äèѳ>Œm]É•+UW×7­¨`,YâræÌfGGË¡C%åä¤6n4ßµké«W醆Éduu눈×ìúW¯>./o.!1ûöígöönŠ©œœYAAéWß]¼øW ‰ÙFcÆüL¬ `±aƒûˆ‹ED¦=zƒXDrr–¡áF!¡iTêêÄÄwúú-ónHÈ+‰öþ}çÄ´´ÜW/îÐÐP–““²·Ÿ·oŸm‹í  `áârMGgå¶mgŒ¶äæ~l/†çÏSÆŽ]! `¤¤dI¡˜ff怠 €•¸B=€…ê*+¼Íƒ,!Ain~Öæ»µµø)‘ÁƒÅˆÇû÷·˜Ì055E77‡˜˜³ªª yy~ÙÙ7‡—{úÔÅ wss˜4I;+ËGVVòƽuuOüý]¾¼3#ÛBa2ÃX¬ð»‚ ¡55!üŸ?²Xá­+©¯¢¢"K§{Áxz:êé©·ˆN÷’—òø±kSSØÁƒ?kj*õóññíÛg››ë»`ÁO Ò¾¾û³³oÊÊJÞ»çÒñ»,VxSSXcãÓÆÆ§»v-ýå—¥Äü;w.y÷ÎçäÉ ŠŠÒD´ Ò7oî++ ºzÕIB‚Òzëݾ}`î܉-&&&^MUU±'rn‡Â»$iåÊÙ¡¡ntº—€?ƒÜf ïÞùÈÈHøùd0‚¯]Û=x°ûs¼zÕÉȈÚö‡> ¢øèî£ëp<3B=))éÝØ±#‰;’¶–œœE¡ˆ©‚@ô…nÜhîáq÷?îß¹sPIIÆÆæ°½ý<##*Œ?êâÅû÷_Y±bÖÒ¥Ó`Á‚ŸàæÍ§zzêÄ_ÆÇÇgŽ£*((ðêUº’’ qŸð}ûþlQI@Àsmí£G'ýäI\ë[«îßeÃ3°´œòë¯W ..CWWíàÁŸ@FFbõê9 N!!AYYÉŽß­¯ots»}çÎßoßæWWמ8±ž˜ÿرµ ©©,!A€½{/¯X1sñb# ‘HTªFë­geEk}{]]µýûíöíûsëÖ3?þøÃáë Ç&&¾eo‡¸¸ ==õK—v«¬¥¥"*J~ú4¡u z­Y3×Òr Q3•ªÁþÅÅE‰Û§s!™ßß4šû™êÞÞÉd2ÙdÊǨ¨âùÁƒ^-f‹ÏÔÓS>\Žý ¦ÏžýcVV¡­í ==u Šöð¸7|ø¢áÃ-\x`ÌÕ‡c-,¦pV—¡¯¯ñoµÿœ ‹ûÿÓ¢­+yôèÅüù“Ø5dg©ªÊsÖÉb±BB^Í™ó#ñ²¨¨LYy(6{Y))ÙÄóòòꢢÏ::j¿»xñÁˆˆ×›>|¸­¦¦H¥jpÎO§ç«üðaÌ‚†­W­3°+- üûow~;»ßZlÎÅÅÇgÓ[ÇÀb±‚‚¢-,&³cà<Á\ZZ¡¤4´ó!!ÔÌ»õ›éuuOêêž,[fråÊ.âùþýv-fã<ܳÕÕ5ØØž2E'00*+Œ·o¯çäøæäøæçß¹xqû—/U22ƒ9K%%½ÓÑQã¨öŸtB´[Wâë»?6–ÎyÒTLŒ\TTÆYgyyuUUœœñòÁƒè™3Ç@\\:vS3)éñ<>>SSSYLŒÜÁ»%%å½ðó;8i’6??_vv!•ªÎžþI±š_¾TUT0TTþIl¯^¥w)ï??ßĉÚ+VÌ"¨ìíÀü¿Ï5[MÌÐ××,+«*/¯6L–câÿÇÐæ‡Ð·Á¼‹POjÑNj!>>CMM±´´‚x0u,ËÎî7Cñ·níOHÈŒŽ~#..ª¨(}úô½ºº†ÂÂÒÝ»/”ÊÉIýùç_õõQQoÜÜn@EEõÛ·ùÙÙ…¿þz5$ä±Ð¬¬99©¦&fëJÒÓ?õ°ƒ133c2›ýý#}}Ÿ99YG^§Ós$$( ÒÀ‘Ã:x÷ãÇ2øðáSZZ®Ía55EqqQöüðoKB!“…üü³³ ó!a‹MýfÚ´­Äè3¶ˆˆ×«VOJzWVVùüyÊ¡C×vìX̹8ÖíÝÿÄ &Fô÷,,,={6 >>ÃÀ@‹˜Él Œš={B×w„Ú€y¡SSS—Ÿ_¢©©Üæ»õõtz޽½›ŒÌ|âqáBо}VV2Ž]-''5þ¤“'ýH$’³ÏSqñ™úúkJJÊ54”½¼œnÜxB¡˜:8œ$ZrfîîwŒŒ¶47³H$øá‡á`a1ÅÁᔬ¬Ù§O_ZT¢®®D"‘8¯Þ¶mÑòå3–-;$-=oÑ¢ƒd²ÐäÉ:þù‹««¯˜ØŒÓ§ï…„WVZXXúùså˜1ªŸÁnG9¬ãw 4§LÑÑÖþyæÌ,èëÿgþŠ Æû÷Eººj‚‚;v,Þºǫ̃Q¶ÉÉÙ¢¢Â#G*´ØzÏž%&%½#NÙ²ÉÊJ}úTF£9ÊÊšÙ۟سÇfíÚyœÛ!!!“½¸²²Êüü‘mÆ@& >¼ÊÑñ4•ºúåËt E„ƒOè¨QÃ44ÚþXê* /G¨«H¤8eüøu[¶Xc—PNŸ¾C÷ñq€ÊJ†®îª»w%ÎC·Dƒ{˜{r?òMŸ>ŽgFˆWÌ›7ñæÍ§˜wÛÄd6766±X¬„„·'Nøúû€ææf[Û£«VÍn7érüéïÛ»uÝÀl%”–V¨ª. v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'adacore_transparent.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = 'favicon.ico' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'GNATColldoc' # -- Options for LaTeX output ------------------------------------------------- # The paper size ('letter' or 'a4'). # latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). # latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', 'GNATColl.tex', u'GNATColl Documentation', u'AdaCore', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Additional stuff for the LaTeX preamble. # latex_preamble = '' # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output ------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'gnatcoll', u'GNATColl Documentation', [u'AdaCore'], 1) ] # -- Options for Epub output -------------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'GNATColl' epub_author = u'AdaCore' epub_publisher = u'AdaCore' epub_copyright = copyright # The language of the text. It defaults to the language option # or en if the language is not set. # epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. # epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. # epub_identifier = '' # A unique identification for the text. # epub_uid = '' # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_post_files = [] # A list of files that should not be packed into the epub file. # epub_exclude_files = [] # The depth of the table of contents in toc.ncx. # epub_tocdepth = 3 # Allow duplicate toc entries. # epub_tocdup = True gnatcoll-core-21.0.0/docs/terminals.rst0000644000175000017500000000446513661715457017675 0ustar nicolasnicolas************************************* **Terminal**: controlling the console ************************************* .. highlight:: ada Applications generally provide user feedback either via full-fledge graphical interfaces, or via a simpler, console-based output. The basic support for console-based output is provided directly via `Ada.Text_IO`. But more advanced features are highly system-dependent, and somewhat tricky to develop. The package `GNATCOLL.Terminal` provide cross-platform support for manipulating colors in terminals, as well as a few basic cursor manipulation subprograms. Colors ====== Most modern terminals support color output, generally with a limit set of colors. On Unix systems, these colors are set by using escape sequences in the output; on Windows systems, these are manipulated by calling functions on a file handle. GNATCOLL will automatically try to guess whether its output is sent to a color enabled terminal. In general, this will be true when outputing to standard output or standard error, and false when outputing to files or to pipes. You can override this default value to force either color support or black-and-white support. Here is an example:: with Ada.Text_IO; use Ada.Text_IO; with GNATCOLL.Terminal; use GNATCOLL.Terminal; procedure Test_Colors is Info : Terminal_Info; begin Info.Init_For_Stdout (Auto); Info.Set_Color (Standard_Output, Blue, Yellow); Put_Line ("A blue on yellow line"); Info.Set_Color (Standard_Output, Style => Reset_All); Put_Line ("Back to standard colors -- much better"); end Test_Colors; Cursors ======= It is often useful for an application to display some progress indicator during long operations. `GNATCOLL.Terminal` provides a limit set of subprograms to do so, as in:: with Ada.Text_IO; use Ada.Text_IO; with GNATCOLL.Terminal; use GNATCOLL.Terminal; procedure Test_Colors is Info : Terminal_Info; begin Info.Init_For_Stdout (Auto); for J in 1 .. 1_000 loop if J mod 10 = 0 then Put ("Processing file" & J'Img & " with long name"); else Put ("Processing file" & J'Img); end if; delay 0.1; Info.Beginning_Of_Line; Info.Clear_To_End_Of_Line; end loop; end Test_Colors; gnatcoll-core-21.0.0/docs/building.rst0000644000175000017500000000556213661715457017473 0ustar nicolasnicolas.. _Building_GNATColl: ***************** Building GNATColl ***************** In the instructions detailed below, it is assumed that you have unpacked the GNATColl package in a temporary directory and that `installdir` is the directory in which you would like to install the selected components. It is further assumed that you have recent functional GNAT compiler, as well as gprbuild. .. _Configuring_the_build_environment: Configuring the build environment ================================= The first step is to configure the build environment. This is done by running the `make setup` command in the root directory of the GNATColl tree. This step is optional if you are satisfied with default values. On Windows, this requires a properly setup Unix-like environment, to provide Unix-like tools. The following variables can be used to configure the build process: General: *prefix* Location of the installation, the default is the running GNAT installation root. *INTEGRATED* Treat prefix as compiler installation: yes or no (default). This is so that installed gnatcoll project can later be referenced as a predefined project of this compiler; this adds a normalized target subdir to prefix. *BUILD* Controls the build options : PROD (default) or DEBUG *PROCESSORS* Parallel compilation (default is 0, which uses all available cores) *TARGET* For cross-compilation, auto-detected for native platforms *SOURCE_DIR* For out-of-tree build *ENABLE_SHARED* Controls whether shared and static-pic library variants should be built: yes (default) or no. If you only intend to use static libraries, specify 'no'. Module-specific: *GNATCOLL_MMAP* Whether MMAP is supported: yes (default) or no; this has no effect on Windows where embedded MMAP implementation is always provided. *GNATCOLL_MADVISE* Whether MADVISE: yes (default) or no; this has no effect on Windows where MADVISE functionality is unavailable .. _Building_GNATColl: Building GNATColl ================= GNATCOLL Core Module can be built using a GPR project file, to build it is as simple as: $ gprbuild gnatcoll.gpr Though, to build all versions of the library (static, relocatable and static-pic) it is simpler to use the provided Makefile: $ make .. _Installing_GNATColl: Installing GNATColl =================== Installing the library is done with the following command:: make install Note that this command does not try to recompile GNATColl, so you must build it first. This command will install all library variants that were built. Your application can now use the GNATColl code through a project file, by adding a ``with`` clause to :file:`gnatcoll.gpr`. If you wish to install in a different location than was specified at configure time, you can override the "prefix" variable from the command line, for instance:: make prefix=/alternate/directory install This does not require any recompilation. gnatcoll-core-21.0.0/docs/config.rst0000644000175000017500000001045213661715457017135 0ustar nicolasnicolas*************************************** **Config**: Parsing configuration files *************************************** .. highlight:: ada `gnatcoll` provides a general framework for reading and manipulating configuration files. These files are in general static configuration for your application, and might be different from the preferences that a user might change interactively. However, it is possible to use them for both cases. There are lots of possible formats for such configuration files: you could chose to use an XML file (but these are in general hard to edit manually), a binary file, or any other format. One format that is found very often is the one used by a lot of Windows applications (the :file:`.ini` file format). `GNATCOLL.Config` is independent from the actual format you are using, and you can add your own parsers compatible with the `GNATCOLL.Config` API. Out of the box, support is provided for :file:`.ini` files, so let's detail this very simply format:: # A single-line comment [Section1] key1 = value key2=value2 [Section2] key1 = value3 Comments are (by default) started with `'#'` signs, but you can configure that and use any prefix you want. The `(key, value)` pairs are then organized into optional sections (if you do not start a section before the first key, that key will be considered as part of the `""` section). A section then extends until the start of the next section. The values associated with the various keys can be strings, integers or booleans. Spaces on the left and right of the values and keys are trimmed, and therefore irrelevant. Support is providing for interpreting the values as file or directory names. In such a case, if a relative name is specified in the configuration file it will be assumed to be relative to the location of the configuration file (by default, but you can also configure that). `GNATCOLL.Config` provides an abstract iterator over a config stream (in general, that stream will be a file, but you could conceptually read it from memory, a socket, or any other location). A specific implementation is provided for file-based streams, which is further specialized to parse :file:`.ini` files. Reading all the values from a configuration file is done with a loop similar to:: declare C : INI_Parser; begin Open (C, "settings.txt"); while not At_End (C) loop Put_Line ("Found key " & Key (C) & " with value " & Value (C)); Next (C); end loop; end; This can be made slightly lighter by using the Ada05 dotted notation. You would only use such a loop in your application if you intend to store the values in various typed constants in your application. But `GNATCOLL.Config` provides a slightly easier interface for this, in the form of a `Config_Pool`. Such a pool is filled by reading a configuration file, and then the values associated with each key can be read at any point during the lifetime of your application. You can also explicitely override the values when needed:: Config : Config_Pool; -- A global variable declare C : INI_Parser; begin Open (C, "settings.txt"); Fill (Config, C); end; Put_Line (Config.Get ("section.key")); -- Ada05 dotted notation Again, the values are by default read as strings, but you can interpret them as integers, booleans or files. A third layer is provided in `GNATCOLL.Config`. This solves the issue of possible typos in code: in the above example, we could have made a typo when writting `"section.key"`. That would only be detected at run time. Another issue is that we might decide to rename the key in the configuration file. We would then have to go through all the application code to find all the places where this key is references (and that can't be based on cross-references generated by the compiler, since that's inside a string). To solve this issue, it is possible to declare a set of constants that represent the keys, and then use these to access the values, solving the two problems above:: Section_Key1 : constant Config_Key := Create ("Key1", "Section"); Section_Key2 : constant Config_Key := Create ("Key2", "Section"); Put_Line (Section_Key1.Get); You then access the value of the keys using the Ada05 dotted notation, providing a very natural syntax. When and if the key is renamed, you then have a single place to change. gnatcoll-core-21.0.0/docs/scripting.rst0000644000175000017500000012727313661715457017704 0ustar nicolasnicolas.. _Embedding_script_languages: *************************************** **Scripts**: Embedding script languages *************************************** In a lot of contexts, you want to give the possibility to users to extend your application. This can be done in several ways: define an Ada API from which they can build dynamically loadable modules, provide the whole source code to your application and let users recompile it, interface with a simpler scripting languages,... Dynamically loadable modules can be loaded on demand, as their name indicate. However, they generally require a relatively complex environment to build, and are somewhat less portable. But when your users are familiar with Ada, they provide a programming environment in which they are comfortable. As usual, changing the module requires recompilation, re-installation,... Providing the source code to your application is generally even more complex for users. This requires an even more complex setup, your application is generally too big for users to dive into, and modifications done by one users are hard to provide to other users, or will be lost when you distribute a new version of your application. The third solution is to embed one or more scripting languages in your application, and export some functions to it. This often requires your users to learn a new language, but these languages are generally relatively simple, and since they are interpreted they are easier to learn in an interactive console. The resulting scripts can easily be redistributed to other users or even distributed with future versions of your application. The module in GNATColl helps you implement the third solution. It was used extensively in the GPS programming environment for its python interface. |Tip| Each of the scripting language is optional This module can be compiled with any of these languages as an optional dependency (except for the shell language, which is always built-in, but is extremely minimal, and doesn't have to be loaded at run time anyway). If the necessary libraries are found on the system, GNATColl will be build with support for the corresponding language, but your application can chose at run time whether or not to activate the support for a specific language. .. index:: test driver .. index:: testing your application |Tip| Use a scripting language to provide an automatic testing framework for your application. The GPS environment uses python command for its *automatic test suite*, including graphical tests such as pressing on a button, selecting a menu,... .. _Supported_languages: Supported languages =================== The module provides built-in support for several scripting languages, and other languages can "easily" be added. Your application does not change when new languages are added, since the interface to export subprograms and classes to the scripting languages is language-neutral, and will automatically export to all known scripting languages. The Core component provides support for the following language: *Shell* This is a very simple-minded scripting language, which doesn't provide flow-control instructions (:ref:`The_Shell_language`). Optional components add support for other languages, e.g. Python. Please refer to the corresponding component's documentation. .. _The_Shell_language: The Shell language ------------------ The shell language was initially developed in the context of the GPS programming environment, as a way to embed scripting commands in XML configuration files. In this language, you can execute any of the commands exported by the application, passing any number of arguments they need. Arguments to function calls can, but need not, be quoted. Quoting is only mandatory when they contain spaces, newline characters, or double-quotes ('"'). To quote an argument, surround it by double-quotes, and precede each double-quote it contains by a backslash character. Another way of quoting is similar to what python provides, which is to triple-quote the argument, i.e. surround it by '"""' on each side. In such a case, any special character (in particular other double-quotes or backslashes) lose their special meaning and are just taken as part of the argument. This is in particular useful when you do not know in advance the contents of the argument you are quoting:: Shell> function_name arg1 "arg 2" """arg 3""" Commands are executed as if on a stack machine: the result of a command is pushed on the stack, and later commands can reference it using `%` following by a number. By default, the number of previous results that are kept is set to 9, and this can only be changed by modifying the source code for GNATColl. The return values are also modified by commands executed internally by your application, and that might have no visible output from the user's point of view. As a result, you should never assume you know what `%1`,... contain unless you just executed a command in the same script:: Shell> function_name arg1 Shell> function2_name %1 In particular, the `%1` syntax is used when emulating object-oriented programming in the shell. A method of a class is just a particular function that contains a '.' in its name, and whose first implicit argument is the instance on which it applies. This instance is generally the result of calling a constructor in an earlier call. Assuming, for instance, that we have exported a class "Base" to the shell from our Ada core, we could use the following code:: Shell> Base arg1 arg2 Shell> Base.method %1 arg1 arg2 to create an instance and call one of its methods. Of course, the shell is not the best language for object-oriented programming, and better languages should be used instead. When an instance has associated properties (which you can export from Ada using `Set_Property`), you access the properties by prefixing its name with "@":: Shell> Base arg1 arg2 # Build new instance Shell> @id %1 # Access its "id" field Shell> @id %1 5 # Set its "id" field Some commands are automatically added to the shell when this scripting language is added to the application. These are .. index:: Function load `Function load (file)` Loads the content of `file` from the disk, and execute each of its lines as a Shell command. This can for instance be used to load scripts when your application is loaded .. index:: Function echo `Function echo (arg...)` This function takes any number of argument, and prints them in the console associated with the language. By default, when in an interactive console, the output of commands is automatically printed to the console. But when you execute a script through `load` above, you need to explicitly call `echo` to make some output visible. .. index:: Function clear_cache `Function clear_cache` This frees the memory used to store the output of previous commands. Calling `%1` afterward will not make sense until further commands are executed. .. _Classes_exported_to_all_languages: Classes exported to all languages --------------------------------- In addition to the functions exported by each specific scripting language, as described above, GNATColl exports the following to all the scripting languages. These are exported when your Ada code calls the Ada procedure `GNATCOLL.Scripts.Register_Standard_Classes`, which should done after you have loaded all the scripting languages. .. index:: Class Console `Class Console` `Console` is a name that you can chose yourself when you call the above Ada procedure. It will be assumed to be `Console` in the rest of this document. This class provides an interface to consoles. A console is an input/output area in your application (whether it is a text area in a graphical application, or simply standard text I/O in text mode). In particular, the python standard output streams `sys.stdin`, `sys.stdout` and `sys.stderr` are redirected to an instance of that class. If you want to see python's error messages or usual output in your application, you must register that class, and define a default console for your scripting language through calls to `GNATCOLL.Scripts.Set_Default_Console`. You can later add new methods to this class, which would be specific to your application. Or you can derive this class into a new class to achieve a similar goal. .. index:: Console.write `Console.write(text)` This method writes `text` to the console associated with the class instance. See the examples delivered with GNATColl for examples on how to create a graphical window and make it into a `Console`. .. index:: Console.clear `Console.clear()` Clears the contents of the console. .. index:: Console.flush `Console.flush()` Does nothing currently, but is needed for compatibility with python. Output through `Console` instances is not buffered anyway. .. index:: Console.isatty `Console.isatty(): Boolean` Whether the console is a pseudo-terminal. This is always wrong in the case of GNATColl. .. index:: Console.read `Console.read([size]): string` Reads at most `size` bytes from the console, and returns the resulting string. .. index:: Console.readline `Console.readline([size]): string` Reads at most `size` lines from the console, and returns them as a single string. .. _Scripts_API: Scripts API =========== This section will give an overview of the API used in the scripts module. The reference documentation for this API is in the source files themselves. In particular, each :file:`.ads` file fully documents all its public API. As described above, GNATColl contains several levels of API. In particular, it provides a low-level interface to python, in the packages `GNATCOLL.Python`. This interface is used by the rest of GNATColl, but is likely too low-level to really be convenient in your applications, since you need to take care of memory management and type conversions by yourself. Instead, GNATColl provides a language-neutral Ada API. Using this API, it is transparent for your application whether you are talking to the Shell, to python, or to another language integrated in GNATColl. The code remains exactly the same, and new scripting languages can be added in later releases of GNATColl without requiring a change in your application. This flexibility is central to the design of GNATColl. In exchange for that flexibility, however, there are language-specific features that cannot be performed through the GNATColl API. At present, this includes for instance exporting functions that return hash tables. But GNATColl doesn't try to export the greatest set of features common to all languages. On the contrary, it tries to fully support all the languages, and provide reasonable fallback for languages that do not support that feature. For instance, named parameters (which are a part of the python language) are fully supported, although the shell language doesn't support them. But that's an implementation detail transparent to your own application. Likewise, your application might decide to always load the python scripting language. If GNATColl wasn't compiled with python support, the corresponding Ada function still exists (and thus your code still compiles), although of course it does nothing. But since the rest of the code is independent of python, this is totally transparent for your application. |Tip| GNATColl comes with some examples, which you can use as a reference when building your own application. See the :file:`/share/examples/gnatcoll` directory. Interfacing your application with the scripting module is a multistep process: * You *must* **initialize** GNATColl and decide which features to load * You *can* create an **interactive console** for the various languages, so that users can perform experiments interactively. This is optional, and you could decide to keep the scripting language has a hidden implementation detail (or just for automatic testing purposes for instance) * You *can* **export** some classes and methods. This is optional, but it doesn't really make sense to just embed a scripting language and export nothing to it. In such a case, you might as well spawn a separate executable. * You *can* load **start up scripts** or plug-ins that users have written to extend your application. .. _Initializing_the_scripting_module: Initializing the scripting module --------------------------------- GNATColl must be initialized properly in order to provide added value to your application. This cannot be done automatically simply by depending on the library, since this initialization requires multiple-step that must be done at specific moments in the initialization of your whole application. This initialization does not depend on whether you have build support for python in GNATColl. The same packages and subprograms are available in all cases, and therefore you do not need conditional compilation in your application to support the various cases. .. _Create_the_scripts_repository: Create the scripts repository ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The type `GNATCOLL.Scripts.Scripts_Repository` will contain various variables common to all the scripting languages, as well as a list of the languages that were activated. This is the starting point for all other types, since from there you have access to everything. You will have only one variable of this type in your application, but it should generally be available from all the code that interfaces with the scripting language. Like the rest of GNATColl, this is a tagged type, which you can extend in your own code. For instance, the GPS programming environment is organized as a kernel and several optional modules. The kernel provides the core functionality of GPS, and should be available from most functions that interface with the scripting languages. Since these functions have very specific profiles, we cannot pass additional arguments to them. One way to work around this limitation is to store the additional arguments (in this case a pointer to the kernel) in a class derived from `Scripts_Repository_Data`. .. highlight:: ada As a result, the code would look like:: with GNATCOLL.Scripts; Repo : Scripts_Repository := new Scripts_Repository_Record; or, in the more complex case of GPS described above:: type Kernel_Scripts_Repository is new Scripts_Repository_Data with record Kernel : ...; end record; Repo : Scripts_Repository := new Kernel_Scripts_Repository' (Scripts_Repository_Data with Kernel => ...); .. _Loading_the_scripting_language: Loading the scripting language ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The next step is to decide which scripting languages should be made available to users. This must be done before any function is exported, since only functions exported after a language has been loaded will be made available in that language. |Note| If for instance python support was build into GNATColl, and if you decide not to make it available to users, your application will still be linked with :file:`libpython`. It is therefore recommended although not mandatory to only build those languages that you will use. This is done through a simple call to one or more subprograms. The following example registers both the shell and python languages:: with GNATCOLL.Scripts.Python; with GNATCOLL.Scripts.Shell; Register_Shell_Scripting (Repo); Register_Python_Scripting (Repo, "MyModule"); .. index:: Procedure Register_Shell_Scripting `Procedure Register_Shell_Scripting (Repo)` This adds support for the shell language. Any class or function that is now exported through GNATColl will be made available in the shell .. index:: Procedure Register_Python_Scripting `Procedure Register_Python_Scripting (Repo, Module_Name)` This adds support for the python language. Any class or function exported from now on will be made available in python, in the module specified by `Module_Name` .. _Exporting_standard_classes: Exporting standard classes ^^^^^^^^^^^^^^^^^^^^^^^^^^ To be fully functional, GNATColl requires some predefined classes to be exported to all languages (:ref:`Classes_exported_to_all_languages`). For instance, the `Console` class is needed for proper interactive with the consoles associated with each language. These classes are created with the following code:: Register_Standard_Classes (Repo, "Console"); This must be done only after all the scripting languages were loaded in the previous step, since otherwise the new classes would not be visible in the other languages. .. index:: Procedure Register_Standard_Classes `Procedure Register_Standard_Classes(Repo,Console_Class)` The second parameter `Console_Class` is the name of the class that is bound to a console, and thus provides input/output support. You can chose this name so that it matches the classes you intend to export later on from your application. .. _Creating_interactive_consoles: Creating interactive consoles ----------------------------- The goal of the scripting module in GNATColl is to work both in text-only applications and graphical applications. However, in both cases applications will need a way to capture the output of scripting languages and display them to the user (at least for errors, to help debugging scripts), and possibly emulate input when a script is waiting for such input. GNATColl solved this problem by using an abstract class `GNATCOLL.Scripts.Virtual_Console_Record` that defines an API for these consoles. This API is used throughout `GNATCOLL.Scripts` whenever input or output has to be performed. |Tip| The :file:`examples/` directory in the GNATColl package shows how to implement a console in text mode and in graphical mode. If you want to provide feedback or interact with users, you will need to provide an actual implementation for these `Virtual_Console`, specific to your application. This could be a graphical text window, or based on `Ada.Text_IO`. The full API is fully documented in :file:`gnatcoll-scripts.ads`, but here is a list of the main subprograms that need to be overriden. .. index:: Virtual_Console.Insert_Text `Virtual_Console.Insert_Text (Txt)` .. index:: Virtual_Console.Insert_Log `Virtual_Console.Insert_Log (Txt)` .. index:: Virtual_Console.Insert_Error `Virtual_Console.Insert_Error (Txt)` These are the various methods for doing output. Error messages could for instance be printed in a different color. Log messages should in general be directed elsewhere, and not be made visible to users unless in special debugging modes. .. index:: Virtual_Console.Insert_Prompt `Virtual_Console.Insert_Prompt (Txt)` This method must display a prompt so that the user knows input is expected. Graphical consoles will in general need to remember where the prompt ended so that they also know where the user input starts .. index:: Virtual_Console.Set_As_Default_Console `Virtual_Console.Set_As_Default_Console (Script)` This method is called when the console becomes the default console for a scripting language. They should in general keep a pointer on that language, so that when the user presses :kbd:`enter` they know which language must execute the command .. index:: Virtual_Console.Read `Virtual_Console.Read (Size, Whole_Line) : String` Read either several characters or whole lines from the console. This is called when the user scripts read from their stdin. .. index:: Virtual_Console.Set_Data_Primitive `Virtual_Console.Set_Data_Primitive (Instance)` .. index:: Virtual_Console.Get_Instance `Virtual_Console.Get_Instance : Console` These two methods are responsible for storing an instance of `Console` into a `GNATCOLL.Scripts.Class_Instance`. Such an instance is what the user manipulates from his scripting language. But when he executes a method, the Ada callback must know how to get the associated `Virtual_Console` back to perform actual operations on it. These methods are implemented using one of the `GNATCOLL.Scripts.Set_Data` and `GNATCOLL.Scripts.Get_Data` operations when in text mode. .. highlight:: ada Once you have created one or more of these console, you can set them as the default console for each of the scripting languages. This way, any input/output done by scripts in this language will interact with that console, instead of being discarded. This is done through code similar to:: Console := GtkConsole.Create (...); Set_Default_Console (Lookup_Scripting_Language (Repo, "python"), Virtual_Console (Console)); Creating a new instance of `Console`, although allowed, will by default create an unusable console. Indeed, depending on your application, you might want to create a new window, reuse an existing one, or do many other things when the user does:: c = Console() As a result, GNATColl does not try to guess the correct behavior, and thus does not export a constructor for the console. So in the above python code, the default python constructor is used. But this constructor does not associate `c` with any actual `Virtual_Console`, and thus any call to a method of `c` will result in an error. To make it possible for users to create their own consoles, you need to export a `Constructor_Method` (see below) for the `Console` class. In addition to your own processing, this constructor needs also to call:: declare Inst : constant Class_Instance := Nth_Arg (Data, 1); begin C := new My_Console_Record; -- or your own type GNATCOLL.Scripts.Set_Data (Inst, C); end .. _Exporting_classes_and_methods: Exporting classes and methods ----------------------------- Once all scripting languages have been loaded, you can start exporting new classes and functions to all the scripting languages. It is important to realize that through a single Ada call, they are exported to all loaded scripting languages, without further work required on your part. .. _Classes_diagram: Classes diagram ^^^^^^^^^^^^^^^ The following diagram shows the dependencies between the major data types defined in :file:`GNATCOLL.Scripts`. Most of these are abstract classes that are implemented by the various scripting languages. Here is a brief description of the role of each type: .. index:: class diagram, script module .. image:: classes.png .. index:: Class Scripts_Repository `Class Scripts_Repository` As we have seen before, this is a type of which there is a single instance in your whole application, and whose main role is to give access to each of the scripting languages (`Lookup_Scripting_Language` function), and to make it possible to register each exported function only once (it then takes care of exporting it to each scripting language). .. index:: Class Scripting_Language `Class Scripting_Language` Instances of this type represent a specific language. It provides various operations to export subprograms, execute commands, create the other types described below,... There should exists a single instance of this class per supported language. This class interacts with the script interpreter (for instance python), and all code executed in python goes through this type, which then executes your Ada callbacks to perform the actual operation. It is also associated with a default console, as described above, so that all input and output of the scripts can be made visible to the user. .. index:: Class Callback_Data `Class Callback_Data` This type is an opaque tagged type that provides a language-independent interface to the scripting language. It gives for instance access to the various parameters passed to your subprogram (`Nth_Arg` functions), allows you to set the return value (`Set_Return_Value` procedure), or raise exceptions (`Set_Error_Msg` procedure),... .. index:: Record Class_Type `Record Class_Type` This type is not tagged, and cannot be extended. It basically represents a class in any of the scripting languages, and is used to create new instances of that class from Ada. .. index:: Class Class_Instance `Class Class_Instance` A class instance represents a specific instance of a class. In general, such an instance is strongly bound to an instance of an Ada type. For instance, if you have a `Foo` type in your application that you wish to export, you would create a `Class_Type` called "Foo", and then the user can create as many instances as he wants of that class, each of which is associated with different values of `Foo` in Ada. Another more specific example is the predefined `Console` class. As we have seen before, this is a `Virtual_Console` in Ada. You could for instance have two graphical windows in your application, each of which is a `Virtual_Console`. In the scripting language, this is exported as a class named `Console`. The user can create two instances of those, each of which is associated with one of your graphical windows. This way, executing `Console.write` on these instances would print the string on their respective graphical window. .. highlight:: python Some scripting languages, in particular python, allow you to store any data within the class instances. In the example above, the user could for instance store the time stamp of the last output in each of the instances. It is therefore important that, as much as possible, you always return the same `Class_Instance` for a given Ada object. See the following python example:: myconsole = Console ("title") # Create new console myconsole.mydata = "20060619" # Any data, really myconsole = Console ("title2") # Create another window myconsole = Console ("title") # Must be same as first, print myconsole.mydata # so that this prints "20060619" .. index:: Class Instance_Property `Class Instance_Property` As we have seen above, a `Class_Instance` is associated in general with an Ada object. This `Instance_Property` tagged type should be extended for each Ada type you want to be able to store in a `Class_Instance`. You can then use the `Set_Data` and `Get_Data` methods of the `Class_Instance` to get and retrieve that associated Ada object. .. index:: Class Subprogram_Record `Class Subprogram_Record` This class represents a callback in the scripting language, that is some code that can be executed when some conditions are met. The exact semantic here depends on each of the programming languages. For instance, if you are programming in python, this is the name of a python method to execute. If you are programming in shell, this is any shell code. .. highlight:: python The idea here is to blend in as smoothly as possible with the usual constructs of each language. For instance, in python one would prefer to write the second line rather than the third:: def on_exit(): pass set_on_exit_callback(on_exit) # Yes, python style set_on_exit_callback("on_exit") # No The last line (using a string as a parameter) would be extremely unusual in python, and would for instance force you to qualify the subprogram name with the name of its namespace (there would be no implicit namespace resolution). To support this special type of parameters, the `Subprogram_Record` type was created in Ada. Although the exact way they are all these types are created is largely irrelevant to your specific application in general, it might be useful for you to override part of the types to provide more advanced features. For instance, GPS redefines its own Shell language, that has basically the same behavior as the Shell language described above but whose `Subprogram_Record` in fact execute internal GPS actions rather than any shell code. .. _Exporting_functions: Exporting functions ^^^^^^^^^^^^^^^^^^^ .. highlight:: ada All functions that you export to the scripting languages will result in a call to an Ada subprogram from your own application. This subprogram must have the following profile:: procedure Handler (Data : in out Callback_Data'Class; Command : String); The first parameter `Data` gives you access to the parameters of the subprogram as passed from the scripting language, and the second parameter `Command` is the name of the command to execute. The idea behind this second parameter is that a single Ada procedure might handle several different script function (for instance because they require common actions to be performed). .. index:: Register_Command `Register_Command (Repo,Command,Min_Args,Max_Args,Handler)` Each of the shell functions is then exported through a call to `Register_Command`. In its simplest form, this procedure takes the following arguments. `Repo` is the scripts repository, so that the command is exported to all the scripting languages. `Command` is the name of the command. `Min_Args` and `Max_Args` are the minimum and maximum number of arguments. Most language allow option parameters, and this is how you specify them. `Handler` is the Ada procedure to call to execute the command. Here is a simple example. It implements a function called `Add`, which takes two integers in parameter, and returns their sum:: Arg1_C : aliased constant String := "arg1"; Arg2_C : aliased constant String := "arg2"; procedure Sum (Data : in out Callback_Data'Class; Command : String) is Arg1, Arg2 : Integer; begin Name_Parameters ((1 => Arg1_C'Access, 2 => Arg2_C'Access)); Arg1 := Nth_Arg (Data, 1); Arg2 := Nth_Arg (Data, 2); Set_Return_Value (Data, Arg1 + Arg2); end Sum; Register_Command (Repo, "sum", 2, 2, Sum'Access); This is not the most useful function to export! Still, it illustrates a number of important concepts. Automatic parameters types ~~~~~~~~~~~~~~~~~~~~~~~~~~ When the command is registered, the number of arguments is specified. This means that GNATColl will check on its own whether the right number of arguments is provided. But the type of these arguments is not specified. Instead, your callback should proceed as if they were correct, and try to retrieve them through one of the numerous `Nth_Arg` functions. In the example above, we assume they are integer. But if one of them was passed as a string, an exception would be raised and sent back to the scripting language to display a proper error message to the user. You have nothing special to do here. Support for named parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Some languages (especially python) support named parameters, ie parameters can be specified in any order on the command line, as long as they are properly identified (very similar to Ada's own capabilities). In the example above, the call to `Name_Parameters` is really optional, but adds this support for your own functions as well. You just have to specify the name of the parameters, and GNATColl will then ensure that when you call `Nth_Arg` the parameter number 1 is really "arg1". For scripting languages that do not support named parameters, this has no effect. Your code can then perform as complex a code as needed, and finally return a value (or not) to the scripting language, through a call to `Set_Return_Value`. .. highlight:: python After the above code has been executed, your users can go to the python console and type for instance:: from MyModule import * # MyModule is the name we declared above print sum (1,2) => 3 print sum () => Error: Wrong number of parameters print sum ("1", 2) => Error: Parameter 1 should be an integer print sum (arg2=2, arg1=1) => 3 .. _Exporting_classes: Exporting classes ^^^^^^^^^^^^^^^^^ Whenever you want to make an Ada type accessible through the scripting languages, you should export it as a class. For object-oriented languages, this would map to the appropriate concept. For other languages, this provides a namespace, so that each method of the class now takes an additional first parameter which is the instance of the class, and the name of the method is prefixed by the class name. .. highlight:: ada Creating a new class is done through a call to `New_Class`, as shown in the example below:: MyClass : Class_Type; MyClass := GNATCOLL.Scripts.New_Class (Repo, "MyClass"); At this stage, nothing is visible in the scripting language, but all the required setup has been done internally so that you can now add methods to this class. You can then register the class methods in the same way that you registered functions. An additional parameter `Class` exists for `Register_Command`. A method is really just a standard function that has an implicit first parameter which is a `Class_Instance`. This extra parameter should not be taken into account in `Min_Args` and `Max_Args`. You can also declare the method as a static method, ie one that doesn't take this extra implicit parameter, and basically just uses the class as a namespace. Some special method names are available. In particular, `Constructor_Method` should be used for the constructor of a class. It is a method that receives, as its first argument, a class instance that has just been created. It should associate that instance with the Ada object it represents. .. highlight:: python Here is a simple example that exports a class. Each instance of this class is associated with a string, passed in parameter to the constructor. The class has a single method `print`, which prints its string parameter prefixed by the instance's string. To start with, here is a python example on what we want to achieve:: c1 = MyClass ("prefix1") c1.print ("foo") => "prefix1 foo" c2 = MyClass () # Using a default prefix c2.print ("foo") => "default foo" .. highlight:: ada Here is the corresponding Ada code:: with GNATCOLL.Scripts.Impl; procedure Handler (Data : **in out** Callback_Data'Class; Command : String) is Inst : Class_Instance := Nth_Arg (Data, 1, MyClass); begin if Command = Constructor_Method then Set_Data (Inst, MyClass, Nth_Arg (Data, 2, "default")); elsif Command = "print" then Insert_Text (Get_Script (Data), null, String'(Get_Data (Inst)) & " " & Nth_Arg (Data, 2)); end if; end Handler; Register_Command (Repo, Constructor_Method, 0, 1, Handler'Access, MyClass); Register_Command (Repo, "print", 1, 1, Handler'Access, MyClass); This example also demonstrates a few concepts: the constructor is declared as a method that takes one optional argument. The default value is in fact passed in the call to `Nth_Arg` and is set to "default". In the handler, we know there is always a first argument which is the instance on which the method applies. The implementation for the constructor stores the prefix in the instance itself, so that several instances can have different prefixes (we can't use global variables, of course, since we don't know in advance how many instances will exist). The implementation for `print` inserts code in the default console for the script (we could of course use `Put_Line` or any other way to output data), and computes the string to output by concatenating the instance's prefix and the parameter to `print`. Note that `Set_Data` and `Get_Data` take the class in parameter, in addition to the class instance. This is needed for proper handling of multiple inheritance: say we have a class `C` that extends two classes `A` and `B`. The Ada code that deals with `A` associates an integer with the class instance, whereas the code that deals with `B` associates a string. Now, if you have an instance of `C` but call a method inherited from `A`, and if `Get_Data` didn't specify the class, there would be a risk that a string would be returned instead of the expected integer. In fact, the proper solution here is that both `A` and `B` store their preferred data at the same time in the instances, but only fetch the one they actually need. Therefore instances of `C` are associated with two datas. Here is a more advanced example that shows how to export an Ada object. Let's assume we have the following Ada type that we want to make available to scripts:: type MyType is record Field : Integer; end record; As you can see, this is not a tagged type, but could certainly be. There is of course no procedure `Set_Data` in :file:`GNATCOLL.Scripts` that enables us to store `MyType` in a `Class_Instance`. This example shows how to write such a procedure. The rest of the code would be similar to the first example, with a constructor that calls `Set_Data`, and methods that call `Get_Data`:: type MyPropsR is new Instance_Property_Record with record Val : MyType; end record; type MyProps is access all MyPropsR'Class; procedure Set_Data (Inst : Class_Instance; Val : MyType) is begin Set_Data (Inst, Get_Name (MyClass), MyPropsR'(Val => Val)); end Set_Data; function Get_Data (Inst : Class_Instance) return MyType is Data : MyProps := MyProps (Instance_Property' (Get_Data (Inst, Get_Name (MyClass)))); begin return Data.Val; end Get_Data; Several aspects worth noting in this example. Each data is associated with a name, not a class as in the previous example. That's in fact the same thing, and mostly for historical reasons. We have to create our own instance of `Instance_Property_Record` to store the data, but the implementation presents no special difficulty. In fact, we don't absolutely need to create `Set_Data` and `Get_Data` and could do everything inline in the method implementation, but it is cleaner this way and easier to reuse. GNATColl is fully responsible for managing the lifetime of the data associated with the class instances and you can override the procedure `Destroy` if you need special memory management. .. _Reusing_class_instances: Reusing class instances ^^^^^^^^^^^^^^^^^^^^^^^ We mentioned above that it is more convenient for users of your exported classes if you always return the same class instance for the same Ada object (for instance a graphical window should always be associated with the same class instance), so that users can associate their own internal data with them. GNATColl provides a few types to facilitate this. In passing, it is worth noting that in fact the Ada objects will be associated with a single instance *per scripting language*, but each language has its own instance. Data is not magically transferred from python to shell! You should store the list of associated instances with your object. The type `GNATCOLL.Scripts.Instance_List_Access` is meant for that purpose, and provides two `Set` and `Get` primitives to retrieve existing instances. The final aspect to consider here is how to return existing instances. This cannot be done from the constructor method, since when it is called it has already received the created instance (this is forced by python, and was done the same for other languages for compatibility reasons). There are two ways to work around that limitation: * Static `get` methods .. highlight:: python With each of your classes, you can export a static method generally called `get` that takes in parameter a way to identify an existing instance, and either return it or create a new one. It is also recommended to disable the constructor, ie force it to raise an error. Let's examine the python code as it would be used:: ed = Editor ("file.adb") # constructor => Error, cannot construct instances ed = Editor.get ("file.adb") => Create a new instance ed2 = Editor.get ("file.adb") => Return existing instance ed == ed2 => True .. highlight:: ada The corresponding Ada code would be something like:: type MyType is record Val : Integer; Inst : Instance_List_Access; end record; type MyTypeAccess is access all MyType; procedure Handler (Data : in out Callback_Data'Class; Cmd : String) is Inst : Class_Instance; Tmp : MyTypeAccess; begin if Cmd = Constructor_Method then Set_Error_Msg (Data, "cannot construct instances"); elsif Cmd = "get" then Tmp := check_if_exists (Nth_Arg (Data, 1)); if Tmp = null then Tmp := create_new_mytype (Nth_Arg (Data, 1)); Tmp.Inst := new Instance_List; end if; Inst := Get (Tmp.Inst.all, Get_Script (Data)); if Inst = No_Class_Instance then Inst := New_Instance (Get_Script (Data), MyClass); Set (Tmp.Inst.all, Get_Script (Data), Inst); Set_Data (Inst, Tmp); end if; Set_Return_Value (Data, Inst); end if; end Handler; * Factory classes The standard way to do this in python, which applies to other languages as well, is to use the Factory design pattern. For this, we need to create one class (`MyClassImpl`) and one factory function (`MyClass`). .. highlight:: python The python code now looks like:: ed = MyClass ("file.adb") # Create new instance => ed is of type MyClassImpl ed = MyClass ("file.adb") # return same instance ed.do_something() It is important to realize that in the call above, we are not calling the constructor of a class, but a function. At the Ada level, the function has basically the same implementation as the one we gave for `get` above. But the python code looks nicer because we do not have these additional `.get()` calls. The name of the class `MyClassImpl` doesn't appear anywhere in the python code, so this is mostly transparent. However, if you have more than one scripting language, in particular for the shell, the code looks less nice in this case:: MyClass "file.adb" => MyClassImpl.do_something %1 and the new name of the class is visible in the method call. .. _Executing_startup_scripts: Executing startup scripts ------------------------- The final step in starting up your application is to load extensions or plug-ins written in one of the scripting languages. There is not much to be said here, except that you should use the `GNATCOLL.Scripts.Execute_File` procedure to do so. .. _Debugging_scripts: Multithreading applications and scripts --------------------------------------- Python itself is not thread-safe. So a single thread can call the python C API at a time. To enforce this, the python interpreter provides a global interpreter lock, which you must acquire before calling the C API, and release when you are done. To simulate multitasking, the python interpreter will in fact release and reacquire the lock every 100 micro-instructions (opcodes in the python virtual machine), to give a chance to run to other tasks. So this is preemptive multitasking. The threads that are created in Ada that do not need access to python do not need any special handling. However, those that need access to python must make a special function call before they first call the python C API, so that python can create a thread-specific data for them. `GNATCOLL.Scripts.Python` contains a number of subprograms to interact with the global interpreter lock of the python engine. The initialization of your application needs to do two extra calls:: Register_Python_Scripting (...); Initialize_Threads_Support; -- Also acquires the lock Begin_Allow_Threads; -- Releases the lock Whenever a task needs to execute python commands (or basically use any subprogram from `GNATCOLL.Scripts`, it needs to do the following:: Ensure_Thread_State; -- Block all python threads ... access to python C API as usual Begin_Allow_Threads; -- Let other python threads run In some cases, the simplest is to get the lock at the beginning of the task, and release it when done. This assumes the task executes fast enough. In other cases, you will need finer grain control over the lock. Debugging scripts ----------------- GNATColl provides a convenient hook to debug your script. By default, a script (python for instance) will call your Ada callback, which might raise errors. Most of the time, the error should indeed be reported to the user, and you can thus raise a standard exception, or call `Set_Error_Msg`. But if you wish to know which script was executing the command, it is generally not doable. You can however activate a trace (:ref:`Logging_information`) called `"PYTHON.TB"` (for "traceback"), which will output the name of the command that is being executed, as well as the full traceback within the python scripts. This will help you locate which script is raising an exception. gnatcoll-core-21.0.0/.gitignore0000644000175000017500000000046213743645322016170 0ustar nicolasnicolasmakefile.setup gnat/ src/sqlite/amalgamation/sqlite3_for_gps docs/_build *.cgpr b__* *.bexch *.a *.d gnatinspect.* obj/ lib/ *.stdout *.stderr *.ali *.gli *.exe *.gcda *.gcno *.gcov *.bexch *.o *.deps *.pyc /gnat_src testsuite/debug testsuite/gcov testsuite/gnatcov testsuite/out examples/library/obj gnatcoll-core-21.0.0/.travis.yml0000644000175000017500000000203013661715457016310 0ustar nicolasnicolas# Global variables language: python python: - "2.7" env: global: - TOOLS_DIR=$HOME/build_tools - INSTALL_DIR=$HOME/build_tools/install - GNAT_INSTALLER=$TOOLS_DIR/gnat-community-installer - GNAT_LINUX_INSTALLER_URL=http://mirrors.cdn.adacore.com/art/5cdffc5409dcd015aaf82626 - GNAT_OSX_INSTALLER_URL=http://mirrors.cdn.adacore.com/art/5ce0322c31e87a8f1d4253fa os: - linux # Cache directory that allows us to not download GNAT GPL every time, speeding # up the process. cache: directories: - $HOME/build_tools install: - export PATH=$INSTALL_DIR/bin:$PATH - sh .travis-install.sh script: # Build gnatcoll-core. Build only the static variant, as only one build is # enough to check for errors: extra builds makes the Travis job longer for no # gain. - make build-static # Run the testsuite. It will rebuild gnatcoll-core in gcov mode before # running the tests, so that we have a coverage report. - (cd testsuite; ./run-tests --gcov) after_success: - bash <(curl -s https://codecov.io/bash) gnatcoll-core-21.0.0/examples/0000755000175000017500000000000013661715457016022 5ustar nicolasnicolasgnatcoll-core-21.0.0/examples/coders/0000755000175000017500000000000013661715457017301 5ustar nicolasnicolasgnatcoll-core-21.0.0/examples/coders/base64_coder.gpr0000644000175000017500000000014113661715457022247 0ustar nicolasnicolaswith "gnatcoll"; project Base64_Coder is for Main use ("base64_coder.adb"); end Base64_Coder; gnatcoll-core-21.0.0/examples/coders/base64-demo0000755000175000017500000000174113661715457021240 0ustar nicolasnicolas# base64_coder is test and example of using the GNATCOLL.Coders.Base64. # base64 utility is system provided base64 encoder/decoder. # Encode original phrase few times and decode it the same number of times with # system and ours encoder/decoder to be sure that they are working the same. # Each encoding by our example decoded by system utility and vise versa. echo Deep coded $1 \ | base64 | base64 | base64 | base64 | base64 | base64 | base64 | base64 \ | ./base64_coder er | ./base64_coder er | ./base64_coder er \ | ./base64_coder ew | ./base64_coder ew | ./base64_coder ew \ | ./base64_coder er | ./base64_coder er | ./base64_coder er \ | base64 | ./base64_coder - | ./base64_coder | base64 -d \ | base64 -d | base64 -d | base64 -d \ | base64 -d | base64 -d | base64 -d \ | base64 -d | base64 -d | base64 -d \ | ./base64_coder dr | ./base64_coder dr | ./base64_coder dr \ | ./base64_coder dw | ./base64_coder dw | ./base64_coder dw \ | ./base64_coder dr | ./base64_coder dw gnatcoll-core-21.0.0/examples/coders/base64_coder.adb0000644000175000017500000001216613661715457022217 0ustar nicolasnicolas------------------------------------------------------------------------------ -- -- -- G N A T C O L L E X A M P L E S -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This example encodes/decodes to/from base64 from standard input to -- standard output. with Ada.Streams; use Ada.Streams; with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO.Text_Streams; use Ada.Text_IO; with GNATCOLL.Coders.Streams; use GNATCOLL.Coders; with GNATCOLL.Coders.Base64; procedure Base64_Coder is Stream : aliased Streams.Stream_Type; Encoder : aliased Base64.Encoder_Type; Decoder : aliased Base64.Decoder_Type; Buffer : Stream_Element_Array (1 .. 1024); Last : Stream_Element_Offset; Action : constant String := (if Argument_Count = 0 then "" else Argument (1)); Back_Input : access Root_Stream_Type'Class; Back_Output : access Root_Stream_Type'Class; Input : access Root_Stream_Type'Class; Output : access Root_Stream_Type'Class; Read_Coder : access Coder_Interface'Class; Write_Coder : access Coder_Interface'Class; Input_File : File_Type; Input_Stream : access Root_Stream_Type'Class; begin Encoder.Initialize (Wrap => 76); Decoder.Initialize; if Argument_Count > 1 then Open (Input_File, In_File, Argument (2)); Input_Stream := Text_Streams.Stream (Input_File); else Input_Stream := Text_Streams.Stream (Current_Input); end if; if Action = "" then -- Encode, decode back, and print to output Back_Input := Input_Stream; Back_Output := Text_Streams.Stream (Current_Output); Input := Stream'Access; Output := Stream'Access; Read_Coder := Encoder'Unchecked_Access; Write_Coder := Decoder'Unchecked_Access; elsif Action = "-" then -- Decode, encode, and print to output Back_Input := Input_Stream; Back_Output := Text_Streams.Stream (Current_Output); Input := Stream'Access; Output := Stream'Access; Read_Coder := Decoder'Unchecked_Access; Write_Coder := Encoder'Unchecked_Access; elsif Action = "er" then -- Encode over read from stream Back_Input := Input_Stream; Input := Stream'Access; Output := Text_Streams.Stream (Current_Output); Read_Coder := Encoder'Unchecked_Access; elsif Action = "dr" then -- Decode over read from stream Back_Input := Input_Stream; Input := Stream'Access; Output := Text_Streams.Stream (Current_Output); Read_Coder := Decoder'Unchecked_Access; elsif Action = "ew" then -- Encode over write to stream Back_Output := Text_Streams.Stream (Current_Output); Output := Stream'Access; Input := Input_Stream; Write_Coder := Encoder'Unchecked_Access; elsif Action = "dw" then -- Decode over write to stream Back_Output := Text_Streams.Stream (Current_Output); Output := Stream'Access; Input := Input_Stream; Write_Coder := Decoder'Unchecked_Access; end if; Stream.Initialize (Read_Coder => Read_Coder, Read_From => Back_Input, Write_Coder => Write_Coder, Write_To => Back_Output, Read_Ends_By => Streams.Partial_Read); loop Input.Read (Buffer, Last); Output.Write (Buffer (1 .. Last)); exit when Last < Buffer'Last; end loop; if Output = Stream'Access then Stream.Flush (Finish); end if; end Base64_Coder; gnatcoll-core-21.0.0/examples/projects/0000755000175000017500000000000013661715457017653 5ustar nicolasnicolasgnatcoll-core-21.0.0/examples/projects/gpr_sources.adb0000644000175000017500000001341013661715457022655 0ustar nicolasnicolas------------------------------------------------------------------------------ -- -- -- G N A T C O L L E X A M P L E S -- -- -- -- Copyright (C) 2019, AdaCore -- -- -- -- This is free software; you can redistribute it and/or modify it -- -- under terms of the GNU General Public License as published by the Free -- -- Software Foundation; either version 3, or (at your option) any later -- -- version. This library is distributed in the hope that it will be useful, -- -- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- -- -- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- ------------------------------------------------------------------------------ -- This example lists all sources belonging to a project. with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with Ada.Text_IO; use Ada.Text_IO; with GNATCOLL.Opt_Parse; use GNATCOLL.Opt_Parse; with GNATCOLL.Projects; use GNATCOLL.Projects; with GNATCOLL.VFS; use GNATCOLL.VFS; procedure GPR_Sources is package Args is Parser : Argument_Parser := Create_Argument_Parser (Help => "Lists all sources belonging to a project."); package Project_File is new Parse_Option (Parser => Parser, Short => "-P", Long => "--project", Arg_Type => Unbounded_String, Default_Val => Null_Unbounded_String, Help => "Project file to use"); package Scenario_Vars is new Parse_Option_List (Parser => Parser, Short => "-X", Long => "--variables", Arg_Type => Unbounded_String, Accumulate => True, Help => "Scenario variables to pass to the project file"); package Target is new Parse_Option (Parser => Parser, Short => "-t", Long => "--target", Arg_Type => Unbounded_String, Default_Val => Null_Unbounded_String, Help => "Custom target to use"); package Runtime is new Parse_Option (Parser => Parser, Long => "--RTS", Arg_Type => Unbounded_String, Default_Val => Null_Unbounded_String, Help => "Custom runtime to use"); package Recursive is new Parse_Flag (Parser => Parser, Short => "-r", Long => "--recursive", Help => "Include sources from all dependencies"); end Args; Env : Project_Environment_Access; Tree : Project_Tree; Files : File_Array_Access; function Init_Project return Boolean; -- Initializes the project tree using command line options supplied by -- Args. Returns True on success, or False otherwise. function Init_Project return Boolean is begin declare Filename : constant String := To_String (Args.Project_File.Get); Target : constant String := To_String (Args.Target.Get); Runtime : constant String := To_String (Args.Runtime.Get); begin if Filename = "" then Put_Line ("Project file not specified."); return False; end if; Initialize (Env); Env.Set_Target_And_Runtime (Target, Runtime); for Assoc of Args.Scenario_Vars.Get loop declare A : constant String := To_String (Assoc); Eq_Index : Natural := A'First; begin while Eq_Index <= A'Length and then A (Eq_Index) /= '=' loop Eq_Index := Eq_Index + 1; end loop; if Eq_Index not in A'Range then Put_Line ("Invalid scenario variable: -X" & A); return False; end if; Change_Environment (Env.all, A (A'First .. Eq_Index - 1), A (Eq_Index + 1 .. A'Last)); end; end loop; Tree.Load (GNATCOLL.VFS.Create (+Filename), Env); if Predefined_Source_Files (Env)'Length = 0 then Put_Line ("Toolchain not found for this target/runtime."); return False; end if; return True; end; end Init_Project; begin if not Args.Parser.Parse then return; end if; if not Init_Project then Put_Line ("Could not initialize project."); return; end if; -- List the source files for project Files := Tree.Root_Project.Source_Files (Recursive => Args.Recursive.Get, Include_Externally_Built => False); for F in Files'Range loop Put_Line (Files (F).Display_Full_Name); end loop; Unchecked_Free (Files); Tree.Unload; Free (Env); end GPR_Sources; gnatcoll-core-21.0.0/examples/projects/gpr_sources.gpr0000644000175000017500000000013713661715457022721 0ustar nicolasnicolaswith "gnatcoll"; project GPR_Sources is for Main use ("gpr_sources.adb"); end GPR_Sources; gnatcoll-core-21.0.0/COPYING.RUNTIME0000644000175000017500000000646013661715457016367 0ustar nicolasnicolasGCC RUNTIME LIBRARY EXCEPTION Version 3.1, 31 March 2009 Copyright (c) 2009 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This GCC Runtime Library Exception ("Exception") is an additional permission under section 7 of the GNU General Public License, version 3 ("GPLv3"). It applies to a given file (the "Runtime Library") that bears a notice placed by the copyright holder of the file stating that the file is governed by GPLv3 along with this Exception. When you use GCC to compile a program, GCC may combine portions of certain GCC header files and runtime libraries with the compiled program. The purpose of this Exception is to allow compilation of non-GPL (including proprietary) programs to use, in this way, the header files and runtime libraries covered by this Exception. 0. Definitions. A file is an "Independent Module" if it either requires the Runtime Library for execution after a Compilation Process, or makes use of an interface provided by the Runtime Library, but is not otherwise based on the Runtime Library. "GCC" means a version of the GNU Compiler Collection, with or without modifications, governed by version 3 (or a specified later version) of the GNU General Public License (GPL) with the option of using any subsequent versions published by the FSF. "GPL-compatible Software" is software whose conditions of propagation, modification and use would permit combination with GCC in accord with the license of GCC. "Target Code" refers to output from any compiler for a real or virtual target processor architecture, in executable form or suitable for input to an assembler, loader, linker and/or execution phase. Notwithstanding that, Target Code does not include data in any format that is used as a compiler intermediate representation, or used for producing a compiler intermediate representation. The "Compilation Process" transforms code entirely represented in non-intermediate languages designed for human-written code, and/or in Java Virtual Machine byte code, into Target Code. Thus, for example, use of source code generators and preprocessors need not be considered part of the Compilation Process, since the Compilation Process can be understood as starting with the output of the generators or preprocessors. A Compilation Process is "Eligible" if it is done using GCC, alone or with other GPL-compatible software, or if it is done without using any work based on GCC. For example, using non-GPL-compatible Software to optimize any GCC intermediate representations would not qualify as an Eligible Compilation Process. 1. Grant of Additional Permission. You have permission to propagate a work of Target Code formed by combining the Runtime Library with Independent Modules, even if such propagation would otherwise violate the terms of GPLv3, provided that all Target Code was generated by Eligible Compilation Processes. You may then convey such a combination under terms of your choice, consistent with the licensing of the Independent Modules. 2. No Weakening of GCC Copyleft. The availability of this Exception does not imply any general presumption that third-party software is unaffected by the copyleft requirements of the license of GCC. gnatcoll-core-21.0.0/appveyor.yml0000644000175000017500000000261513661715457016600 0ustar nicolasnicolasversion: 1.0.{build} environment: PYTHON: C:\Python27 cache: - C:\AdaDownloads install: - ps: $env:COMPILER_DIR = "C:\AdaCompiler" - ps: $env:CACHE_DIR = "C:\AdaDownloads" - ps: $env:COMPILER_INSTALLER = $env:CACHE_DIR + "\gnat-gpl-2017.exe" - ps: md -f $env:COMPILER_DIR - ps: md -f $env:CACHE_DIR - ps: > If (Test-Path $env:COMPILER_INSTALLER) { echo compiler already in cache } Else { (new-object net.webclient).DownloadFile( 'http://mirrors.cdn.adacore.com/art/591c97f0a3f5d779ee51082d', $env:COMPILER_INSTALLER) } - ps: dir $env:CACHE_DIR - cmd: cmd /c start /wait %COMPILER_INSTALLER% /S /D=%COMPILER_DIR% - ps: $env:Path = $env:COMPILER_DIR + "\bin;" + $env:Path - cmd: cd - cmd: dir - cmd: git clone https://github.com/AdaCore/gprbuild libgpr-src - cmd: cd libgpr-src - cmd: gprbuild -p -m -j0 -XBUILD=production -XLIBRARY_TYPE=relocatable -XXMLADA_BUILD=relocatable -P gpr/gpr.gpr - cmd: gprinstall -p -f -XBUILD=production --install-name=gpr --build-var=LIBRARY_TYPE -XLIBRARY_TYPE=relocatable -XXMLADA_BUILD=relocatable --build-name=relocatable -P gpr/gpr.gpr - cmd: cd .. - cmd: > gprbuild.exe -p -m -j0 -XGNATCOLL_OS=windows -XGNATCOLL_VERSION=0.0 -XBUILD=PROD -XLIBRARY_TYPE=relocatable -XXMLADA_BUILD=relocatable -P gnatcoll.gpr build: off