xref: /dokuwiki/vendor/composer/InstalledVersions.php (revision d3233986baa7dfe44490b805ae2e4296fad59401)
16cb05674SAndreas Gohr<?php
26cb05674SAndreas Gohr
3*d3233986SAndreas Gohr/*
4*d3233986SAndreas Gohr * This file is part of Composer.
5*d3233986SAndreas Gohr *
6*d3233986SAndreas Gohr * (c) Nils Adermann <naderman@naderman.de>
7*d3233986SAndreas Gohr *     Jordi Boggiano <j.boggiano@seld.be>
8*d3233986SAndreas Gohr *
9*d3233986SAndreas Gohr * For the full copyright and license information, please view the LICENSE
10*d3233986SAndreas Gohr * file that was distributed with this source code.
11*d3233986SAndreas Gohr */
126cb05674SAndreas Gohr
136cb05674SAndreas Gohrnamespace Composer;
146cb05674SAndreas Gohr
156cb05674SAndreas Gohruse Composer\Autoload\ClassLoader;
166cb05674SAndreas Gohruse Composer\Semver\VersionParser;
176cb05674SAndreas Gohr
18*d3233986SAndreas Gohr/**
19*d3233986SAndreas Gohr * This class is copied in every Composer installed project and available to all
20*d3233986SAndreas Gohr *
21*d3233986SAndreas Gohr * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22*d3233986SAndreas Gohr *
23*d3233986SAndreas Gohr * To require its presence, you can require `composer-runtime-api ^2.0`
24*d3233986SAndreas Gohr *
25*d3233986SAndreas Gohr * @final
26*d3233986SAndreas Gohr */
276cb05674SAndreas Gohrclass InstalledVersions
286cb05674SAndreas Gohr{
29*d3233986SAndreas Gohr    /**
30*d3233986SAndreas Gohr     * @var mixed[]|null
31*d3233986SAndreas Gohr     * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
32*d3233986SAndreas Gohr     */
33*d3233986SAndreas Gohr    private static $installed;
34*d3233986SAndreas Gohr
35*d3233986SAndreas Gohr    /**
36*d3233986SAndreas Gohr     * @var bool|null
37*d3233986SAndreas Gohr     */
386cb05674SAndreas Gohr    private static $canGetVendors;
39*d3233986SAndreas Gohr
40*d3233986SAndreas Gohr    /**
41*d3233986SAndreas Gohr     * @var array[]
42*d3233986SAndreas Gohr     * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
43*d3233986SAndreas Gohr     */
446cb05674SAndreas Gohr    private static $installedByVendor = array();
456cb05674SAndreas Gohr
46*d3233986SAndreas Gohr    /**
47*d3233986SAndreas Gohr     * Returns a list of all package names which are present, either by being installed, replaced or provided
48*d3233986SAndreas Gohr     *
49*d3233986SAndreas Gohr     * @return string[]
50*d3233986SAndreas Gohr     * @psalm-return list<string>
51*d3233986SAndreas Gohr     */
526cb05674SAndreas Gohr    public static function getInstalledPackages()
536cb05674SAndreas Gohr    {
546cb05674SAndreas Gohr        $packages = array();
556cb05674SAndreas Gohr        foreach (self::getInstalled() as $installed) {
566cb05674SAndreas Gohr            $packages[] = array_keys($installed['versions']);
576cb05674SAndreas Gohr        }
586cb05674SAndreas Gohr
596cb05674SAndreas Gohr        if (1 === \count($packages)) {
606cb05674SAndreas Gohr            return $packages[0];
616cb05674SAndreas Gohr        }
626cb05674SAndreas Gohr
636cb05674SAndreas Gohr        return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
646cb05674SAndreas Gohr    }
656cb05674SAndreas Gohr
66*d3233986SAndreas Gohr    /**
67*d3233986SAndreas Gohr     * Returns a list of all package names with a specific type e.g. 'library'
68*d3233986SAndreas Gohr     *
69*d3233986SAndreas Gohr     * @param  string   $type
70*d3233986SAndreas Gohr     * @return string[]
71*d3233986SAndreas Gohr     * @psalm-return list<string>
72*d3233986SAndreas Gohr     */
73*d3233986SAndreas Gohr    public static function getInstalledPackagesByType($type)
74*d3233986SAndreas Gohr    {
75*d3233986SAndreas Gohr        $packagesByType = array();
766cb05674SAndreas Gohr
77*d3233986SAndreas Gohr        foreach (self::getInstalled() as $installed) {
78*d3233986SAndreas Gohr            foreach ($installed['versions'] as $name => $package) {
79*d3233986SAndreas Gohr                if (isset($package['type']) && $package['type'] === $type) {
80*d3233986SAndreas Gohr                    $packagesByType[] = $name;
81*d3233986SAndreas Gohr                }
82*d3233986SAndreas Gohr            }
83*d3233986SAndreas Gohr        }
846cb05674SAndreas Gohr
85*d3233986SAndreas Gohr        return $packagesByType;
86*d3233986SAndreas Gohr    }
876cb05674SAndreas Gohr
88*d3233986SAndreas Gohr    /**
89*d3233986SAndreas Gohr     * Checks whether the given package is installed
90*d3233986SAndreas Gohr     *
91*d3233986SAndreas Gohr     * This also returns true if the package name is provided or replaced by another package
92*d3233986SAndreas Gohr     *
93*d3233986SAndreas Gohr     * @param  string $packageName
94*d3233986SAndreas Gohr     * @param  bool   $includeDevRequirements
95*d3233986SAndreas Gohr     * @return bool
96*d3233986SAndreas Gohr     */
97*d3233986SAndreas Gohr    public static function isInstalled($packageName, $includeDevRequirements = true)
986cb05674SAndreas Gohr    {
996cb05674SAndreas Gohr        foreach (self::getInstalled() as $installed) {
1006cb05674SAndreas Gohr            if (isset($installed['versions'][$packageName])) {
101*d3233986SAndreas Gohr                return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
1026cb05674SAndreas Gohr            }
1036cb05674SAndreas Gohr        }
1046cb05674SAndreas Gohr
1056cb05674SAndreas Gohr        return false;
1066cb05674SAndreas Gohr    }
1076cb05674SAndreas Gohr
108*d3233986SAndreas Gohr    /**
109*d3233986SAndreas Gohr     * Checks whether the given package satisfies a version constraint
110*d3233986SAndreas Gohr     *
111*d3233986SAndreas Gohr     * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
112*d3233986SAndreas Gohr     *
113*d3233986SAndreas Gohr     *   Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
114*d3233986SAndreas Gohr     *
115*d3233986SAndreas Gohr     * @param  VersionParser $parser      Install composer/semver to have access to this class and functionality
116*d3233986SAndreas Gohr     * @param  string        $packageName
117*d3233986SAndreas Gohr     * @param  string|null   $constraint  A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
118*d3233986SAndreas Gohr     * @return bool
119*d3233986SAndreas Gohr     */
1206cb05674SAndreas Gohr    public static function satisfies(VersionParser $parser, $packageName, $constraint)
1216cb05674SAndreas Gohr    {
1226cb05674SAndreas Gohr        $constraint = $parser->parseConstraints($constraint);
1236cb05674SAndreas Gohr        $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
1246cb05674SAndreas Gohr
1256cb05674SAndreas Gohr        return $provided->matches($constraint);
1266cb05674SAndreas Gohr    }
1276cb05674SAndreas Gohr
128*d3233986SAndreas Gohr    /**
129*d3233986SAndreas Gohr     * Returns a version constraint representing all the range(s) which are installed for a given package
130*d3233986SAndreas Gohr     *
131*d3233986SAndreas Gohr     * It is easier to use this via isInstalled() with the $constraint argument if you need to check
132*d3233986SAndreas Gohr     * whether a given version of a package is installed, and not just whether it exists
133*d3233986SAndreas Gohr     *
134*d3233986SAndreas Gohr     * @param  string $packageName
135*d3233986SAndreas Gohr     * @return string Version constraint usable with composer/semver
136*d3233986SAndreas Gohr     */
1376cb05674SAndreas Gohr    public static function getVersionRanges($packageName)
1386cb05674SAndreas Gohr    {
1396cb05674SAndreas Gohr        foreach (self::getInstalled() as $installed) {
1406cb05674SAndreas Gohr            if (!isset($installed['versions'][$packageName])) {
1416cb05674SAndreas Gohr                continue;
1426cb05674SAndreas Gohr            }
1436cb05674SAndreas Gohr
1446cb05674SAndreas Gohr            $ranges = array();
1456cb05674SAndreas Gohr            if (isset($installed['versions'][$packageName]['pretty_version'])) {
1466cb05674SAndreas Gohr                $ranges[] = $installed['versions'][$packageName]['pretty_version'];
1476cb05674SAndreas Gohr            }
1486cb05674SAndreas Gohr            if (array_key_exists('aliases', $installed['versions'][$packageName])) {
1496cb05674SAndreas Gohr                $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
1506cb05674SAndreas Gohr            }
1516cb05674SAndreas Gohr            if (array_key_exists('replaced', $installed['versions'][$packageName])) {
1526cb05674SAndreas Gohr                $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
1536cb05674SAndreas Gohr            }
1546cb05674SAndreas Gohr            if (array_key_exists('provided', $installed['versions'][$packageName])) {
1556cb05674SAndreas Gohr                $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
1566cb05674SAndreas Gohr            }
1576cb05674SAndreas Gohr
1586cb05674SAndreas Gohr            return implode(' || ', $ranges);
1596cb05674SAndreas Gohr        }
1606cb05674SAndreas Gohr
1616cb05674SAndreas Gohr        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
1626cb05674SAndreas Gohr    }
1636cb05674SAndreas Gohr
164*d3233986SAndreas Gohr    /**
165*d3233986SAndreas Gohr     * @param  string      $packageName
166*d3233986SAndreas Gohr     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
167*d3233986SAndreas Gohr     */
1686cb05674SAndreas Gohr    public static function getVersion($packageName)
1696cb05674SAndreas Gohr    {
1706cb05674SAndreas Gohr        foreach (self::getInstalled() as $installed) {
1716cb05674SAndreas Gohr            if (!isset($installed['versions'][$packageName])) {
1726cb05674SAndreas Gohr                continue;
1736cb05674SAndreas Gohr            }
1746cb05674SAndreas Gohr
1756cb05674SAndreas Gohr            if (!isset($installed['versions'][$packageName]['version'])) {
1766cb05674SAndreas Gohr                return null;
1776cb05674SAndreas Gohr            }
1786cb05674SAndreas Gohr
1796cb05674SAndreas Gohr            return $installed['versions'][$packageName]['version'];
1806cb05674SAndreas Gohr        }
1816cb05674SAndreas Gohr
1826cb05674SAndreas Gohr        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
1836cb05674SAndreas Gohr    }
1846cb05674SAndreas Gohr
185*d3233986SAndreas Gohr    /**
186*d3233986SAndreas Gohr     * @param  string      $packageName
187*d3233986SAndreas Gohr     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
188*d3233986SAndreas Gohr     */
1896cb05674SAndreas Gohr    public static function getPrettyVersion($packageName)
1906cb05674SAndreas Gohr    {
1916cb05674SAndreas Gohr        foreach (self::getInstalled() as $installed) {
1926cb05674SAndreas Gohr            if (!isset($installed['versions'][$packageName])) {
1936cb05674SAndreas Gohr                continue;
1946cb05674SAndreas Gohr            }
1956cb05674SAndreas Gohr
1966cb05674SAndreas Gohr            if (!isset($installed['versions'][$packageName]['pretty_version'])) {
1976cb05674SAndreas Gohr                return null;
1986cb05674SAndreas Gohr            }
1996cb05674SAndreas Gohr
2006cb05674SAndreas Gohr            return $installed['versions'][$packageName]['pretty_version'];
2016cb05674SAndreas Gohr        }
2026cb05674SAndreas Gohr
2036cb05674SAndreas Gohr        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
2046cb05674SAndreas Gohr    }
2056cb05674SAndreas Gohr
206*d3233986SAndreas Gohr    /**
207*d3233986SAndreas Gohr     * @param  string      $packageName
208*d3233986SAndreas Gohr     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
209*d3233986SAndreas Gohr     */
2106cb05674SAndreas Gohr    public static function getReference($packageName)
2116cb05674SAndreas Gohr    {
2126cb05674SAndreas Gohr        foreach (self::getInstalled() as $installed) {
2136cb05674SAndreas Gohr            if (!isset($installed['versions'][$packageName])) {
2146cb05674SAndreas Gohr                continue;
2156cb05674SAndreas Gohr            }
2166cb05674SAndreas Gohr
2176cb05674SAndreas Gohr            if (!isset($installed['versions'][$packageName]['reference'])) {
2186cb05674SAndreas Gohr                return null;
2196cb05674SAndreas Gohr            }
2206cb05674SAndreas Gohr
2216cb05674SAndreas Gohr            return $installed['versions'][$packageName]['reference'];
2226cb05674SAndreas Gohr        }
2236cb05674SAndreas Gohr
2246cb05674SAndreas Gohr        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
2256cb05674SAndreas Gohr    }
2266cb05674SAndreas Gohr
227*d3233986SAndreas Gohr    /**
228*d3233986SAndreas Gohr     * @param  string      $packageName
229*d3233986SAndreas Gohr     * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
230*d3233986SAndreas Gohr     */
231*d3233986SAndreas Gohr    public static function getInstallPath($packageName)
232*d3233986SAndreas Gohr    {
233*d3233986SAndreas Gohr        foreach (self::getInstalled() as $installed) {
234*d3233986SAndreas Gohr            if (!isset($installed['versions'][$packageName])) {
235*d3233986SAndreas Gohr                continue;
236*d3233986SAndreas Gohr            }
2376cb05674SAndreas Gohr
238*d3233986SAndreas Gohr            return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
239*d3233986SAndreas Gohr        }
2406cb05674SAndreas Gohr
241*d3233986SAndreas Gohr        throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
242*d3233986SAndreas Gohr    }
2436cb05674SAndreas Gohr
244*d3233986SAndreas Gohr    /**
245*d3233986SAndreas Gohr     * @return array
246*d3233986SAndreas Gohr     * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
247*d3233986SAndreas Gohr     */
2486cb05674SAndreas Gohr    public static function getRootPackage()
2496cb05674SAndreas Gohr    {
2506cb05674SAndreas Gohr        $installed = self::getInstalled();
2516cb05674SAndreas Gohr
2526cb05674SAndreas Gohr        return $installed[0]['root'];
2536cb05674SAndreas Gohr    }
2546cb05674SAndreas Gohr
255*d3233986SAndreas Gohr    /**
256*d3233986SAndreas Gohr     * Returns the raw installed.php data for custom implementations
257*d3233986SAndreas Gohr     *
258*d3233986SAndreas Gohr     * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
259*d3233986SAndreas Gohr     * @return array[]
260*d3233986SAndreas Gohr     * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
261*d3233986SAndreas Gohr     */
2626cb05674SAndreas Gohr    public static function getRawData()
2636cb05674SAndreas Gohr    {
264*d3233986SAndreas Gohr        @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
265*d3233986SAndreas Gohr
266*d3233986SAndreas Gohr        if (null === self::$installed) {
267*d3233986SAndreas Gohr            // only require the installed.php file if this file is loaded from its dumped location,
268*d3233986SAndreas Gohr            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
269*d3233986SAndreas Gohr            if (substr(__DIR__, -8, 1) !== 'C') {
270*d3233986SAndreas Gohr                self::$installed = include __DIR__ . '/installed.php';
271*d3233986SAndreas Gohr            } else {
272*d3233986SAndreas Gohr                self::$installed = array();
273*d3233986SAndreas Gohr            }
274*d3233986SAndreas Gohr        }
275*d3233986SAndreas Gohr
2766cb05674SAndreas Gohr        return self::$installed;
2776cb05674SAndreas Gohr    }
2786cb05674SAndreas Gohr
279*d3233986SAndreas Gohr    /**
280*d3233986SAndreas Gohr     * Returns the raw data of all installed.php which are currently loaded for custom implementations
281*d3233986SAndreas Gohr     *
282*d3233986SAndreas Gohr     * @return array[]
283*d3233986SAndreas Gohr     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
284*d3233986SAndreas Gohr     */
285*d3233986SAndreas Gohr    public static function getAllRawData()
286*d3233986SAndreas Gohr    {
287*d3233986SAndreas Gohr        return self::getInstalled();
288*d3233986SAndreas Gohr    }
2896cb05674SAndreas Gohr
290*d3233986SAndreas Gohr    /**
291*d3233986SAndreas Gohr     * Lets you reload the static array from another file
292*d3233986SAndreas Gohr     *
293*d3233986SAndreas Gohr     * This is only useful for complex integrations in which a project needs to use
294*d3233986SAndreas Gohr     * this class but then also needs to execute another project's autoloader in process,
295*d3233986SAndreas Gohr     * and wants to ensure both projects have access to their version of installed.php.
296*d3233986SAndreas Gohr     *
297*d3233986SAndreas Gohr     * A typical case would be PHPUnit, where it would need to make sure it reads all
298*d3233986SAndreas Gohr     * the data it needs from this class, then call reload() with
299*d3233986SAndreas Gohr     * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
300*d3233986SAndreas Gohr     * the project in which it runs can then also use this class safely, without
301*d3233986SAndreas Gohr     * interference between PHPUnit's dependencies and the project's dependencies.
302*d3233986SAndreas Gohr     *
303*d3233986SAndreas Gohr     * @param  array[] $data A vendor/composer/installed.php data set
304*d3233986SAndreas Gohr     * @return void
305*d3233986SAndreas Gohr     *
306*d3233986SAndreas Gohr     * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
307*d3233986SAndreas Gohr     */
3086cb05674SAndreas Gohr    public static function reload($data)
3096cb05674SAndreas Gohr    {
3106cb05674SAndreas Gohr        self::$installed = $data;
3116cb05674SAndreas Gohr        self::$installedByVendor = array();
3126cb05674SAndreas Gohr    }
3136cb05674SAndreas Gohr
314*d3233986SAndreas Gohr    /**
315*d3233986SAndreas Gohr     * @return array[]
316*d3233986SAndreas Gohr     * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
317*d3233986SAndreas Gohr     */
3186cb05674SAndreas Gohr    private static function getInstalled()
3196cb05674SAndreas Gohr    {
3206cb05674SAndreas Gohr        if (null === self::$canGetVendors) {
3216cb05674SAndreas Gohr            self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
3226cb05674SAndreas Gohr        }
3236cb05674SAndreas Gohr
3246cb05674SAndreas Gohr        $installed = array();
3256cb05674SAndreas Gohr
3266cb05674SAndreas Gohr        if (self::$canGetVendors) {
3276cb05674SAndreas Gohr            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
3286cb05674SAndreas Gohr                if (isset(self::$installedByVendor[$vendorDir])) {
3296cb05674SAndreas Gohr                    $installed[] = self::$installedByVendor[$vendorDir];
3306cb05674SAndreas Gohr                } elseif (is_file($vendorDir.'/composer/installed.php')) {
3316cb05674SAndreas Gohr                    $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
332*d3233986SAndreas Gohr                    if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
333*d3233986SAndreas Gohr                        self::$installed = $installed[count($installed) - 1];
334*d3233986SAndreas Gohr                    }
3356cb05674SAndreas Gohr                }
3366cb05674SAndreas Gohr            }
3376cb05674SAndreas Gohr        }
3386cb05674SAndreas Gohr
339*d3233986SAndreas Gohr        if (null === self::$installed) {
340*d3233986SAndreas Gohr            // only require the installed.php file if this file is loaded from its dumped location,
341*d3233986SAndreas Gohr            // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
342*d3233986SAndreas Gohr            if (substr(__DIR__, -8, 1) !== 'C') {
343*d3233986SAndreas Gohr                self::$installed = require __DIR__ . '/installed.php';
344*d3233986SAndreas Gohr            } else {
345*d3233986SAndreas Gohr                self::$installed = array();
346*d3233986SAndreas Gohr            }
347*d3233986SAndreas Gohr        }
3486cb05674SAndreas Gohr        $installed[] = self::$installed;
3496cb05674SAndreas Gohr
3506cb05674SAndreas Gohr        return $installed;
3516cb05674SAndreas Gohr    }
3526cb05674SAndreas Gohr}
353