LCOV - code coverage report
Current view: top level - lang/cpp/src - verificationresult.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 62 292 21.2 %
Date: 2016-09-12 13:07:23 Functions: 15 56 26.8 %

          Line data    Source code
       1             : /*
       2             :   verificationresult.cpp - wraps a gpgme verify result
       3             :   Copyright (C) 2004 Klarälvdalens Datakonsult AB
       4             : 
       5             :   This file is part of GPGME++.
       6             : 
       7             :   GPGME++ is free software; you can redistribute it and/or
       8             :   modify it under the terms of the GNU Library General Public
       9             :   License as published by the Free Software Foundation; either
      10             :   version 2 of the License, or (at your option) any later version.
      11             : 
      12             :   GPGME++ is distributed in the hope that it will be useful,
      13             :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :   GNU Library General Public License for more details.
      16             : 
      17             :   You should have received a copy of the GNU Library General Public License
      18             :   along with GPGME++; see the file COPYING.LIB.  If not, write to the
      19             :   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
      20             :   Boston, MA 02110-1301, USA.
      21             : */
      22             : 
      23             : #include <verificationresult.h>
      24             : #include <notation.h>
      25             : #include "result_p.h"
      26             : #include "util.h"
      27             : #include "key.h"
      28             : 
      29             : #include <gpgme.h>
      30             : 
      31             : #include <istream>
      32             : #include <algorithm>
      33             : #include <iterator>
      34             : #include <string>
      35             : #include <cstring>
      36             : #include <cstdlib>
      37             : 
      38             : #include <string.h>
      39             : 
      40             : class GpgME::VerificationResult::Private
      41             : {
      42             : public:
      43           9 :     explicit Private(const gpgme_verify_result_t r)
      44           9 :     {
      45           9 :         if (!r) {
      46           9 :             return;
      47             :         }
      48           9 :         if (r->file_name) {
      49           3 :             file_name = r->file_name;
      50             :         }
      51             :         // copy recursively, using compiler-generated copy ctor.
      52             :         // We just need to handle the pointers in the structs:
      53          18 :         for (gpgme_signature_t is = r->signatures ; is ; is = is->next) {
      54           9 :             gpgme_signature_t scopy = new _gpgme_signature(*is);
      55           9 :             if (is->fpr) {
      56           9 :                 scopy->fpr = strdup(is->fpr);
      57             :             }
      58             : // PENDING(marc) why does this crash on Windows in strdup()?
      59             : # ifndef _WIN32
      60           9 :             if (is->pka_address) {
      61           0 :                 scopy->pka_address = strdup(is->pka_address);
      62             :             }
      63             : # else
      64             :             scopy->pka_address = 0;
      65             : # endif
      66           9 :             scopy->next = 0;
      67           9 :             sigs.push_back(scopy);
      68             :             // copy keys
      69           9 :             if (scopy->key) {
      70           9 :                 keys.push_back(Key(scopy->key, true));
      71             :             }
      72             :             // copy notations:
      73           9 :             nota.push_back(std::vector<Nota>());
      74           9 :             purls.push_back(0);
      75           9 :             for (gpgme_sig_notation_t in = is->notations ; in ; in = in->next) {
      76           0 :                 if (!in->name) {
      77           0 :                     if (in->value) {
      78           0 :                         purls.back() = strdup(in->value);   // policy url
      79             :                     }
      80           0 :                     continue;
      81             :                 }
      82           0 :                 Nota n = { 0, 0, in->flags };
      83           0 :                 n.name = strdup(in->name);
      84           0 :                 if (in->value) {
      85           0 :                     n.value = strdup(in->value);
      86             :                 }
      87           0 :                 nota.back().push_back(n);
      88             :             }
      89             :         }
      90             :     }
      91           9 :     ~Private()
      92           9 :     {
      93          18 :         for (std::vector<gpgme_signature_t>::iterator it = sigs.begin() ; it != sigs.end() ; ++it) {
      94           9 :             std::free((*it)->fpr);
      95           9 :             std::free((*it)->pka_address);
      96           9 :             delete *it; *it = 0;
      97             :         }
      98          18 :         for (std::vector< std::vector<Nota> >::iterator it = nota.begin() ; it != nota.end() ; ++it) {
      99           9 :             for (std::vector<Nota>::iterator jt = it->begin() ; jt != it->end() ; ++jt) {
     100           0 :                 std::free(jt->name);  jt->name = 0;
     101           0 :                 std::free(jt->value); jt->value = 0;
     102             :             }
     103             :         }
     104           9 :         std::for_each(purls.begin(), purls.end(), &std::free);
     105           9 :     }
     106             : 
     107             :     struct Nota {
     108             :         char *name;
     109             :         char *value;
     110             :         gpgme_sig_notation_flags_t flags;
     111             :     };
     112             : 
     113             :     std::vector<gpgme_signature_t> sigs;
     114             :     std::vector< std::vector<Nota> > nota;
     115             :     std::vector<GpgME::Key> keys;
     116             :     std::vector<char *> purls;
     117             :     std::string file_name;
     118             : };
     119             : 
     120           0 : GpgME::VerificationResult::VerificationResult(gpgme_ctx_t ctx, int error)
     121           0 :     : GpgME::Result(error), d()
     122             : {
     123           0 :     init(ctx);
     124           0 : }
     125             : 
     126           9 : GpgME::VerificationResult::VerificationResult(gpgme_ctx_t ctx, const Error &error)
     127           9 :     : GpgME::Result(error), d()
     128             : {
     129           9 :     init(ctx);
     130           9 : }
     131             : 
     132           9 : void GpgME::VerificationResult::init(gpgme_ctx_t ctx)
     133             : {
     134           9 :     if (!ctx) {
     135           0 :         return;
     136             :     }
     137           9 :     gpgme_verify_result_t res = gpgme_op_verify_result(ctx);
     138           9 :     if (!res) {
     139           0 :         return;
     140             :     }
     141           9 :     d.reset(new Private(res));
     142             : }
     143             : 
     144          21 : make_standard_stuff(VerificationResult)
     145             : 
     146           0 : const char *GpgME::VerificationResult::fileName() const
     147             : {
     148           0 :     return d ? d->file_name.c_str() : 0 ;
     149             : }
     150             : 
     151           9 : unsigned int GpgME::VerificationResult::numSignatures() const
     152             : {
     153           9 :     return d ? d->sigs.size() : 0 ;
     154             : }
     155             : 
     156           0 : GpgME::Signature GpgME::VerificationResult::signature(unsigned int idx) const
     157             : {
     158           0 :     return Signature(d, idx);
     159             : }
     160             : 
     161           9 : std::vector<GpgME::Signature> GpgME::VerificationResult::signatures() const
     162             : {
     163           9 :     if (!d) {
     164           0 :         return std::vector<Signature>();
     165             :     }
     166           9 :     std::vector<Signature> result;
     167           9 :     result.reserve(d->sigs.size());
     168          18 :     for (unsigned int i = 0 ; i < d->sigs.size() ; ++i) {
     169           9 :         result.push_back(Signature(d, i));
     170             :     }
     171           9 :     return result;
     172             : }
     173             : 
     174           9 : GpgME::Signature::Signature(const std::shared_ptr<VerificationResult::Private> &parent, unsigned int i)
     175           9 :     : d(parent), idx(i)
     176             : {
     177           9 : }
     178             : 
     179           0 : GpgME::Signature::Signature() : d(), idx(0) {}
     180             : 
     181          26 : bool GpgME::Signature::isNull() const
     182             : {
     183          26 :     return !d || idx >= d->sigs.size() ;
     184             : }
     185             : 
     186           0 : GpgME::Signature::Summary GpgME::Signature::summary() const
     187             : {
     188           0 :     if (isNull()) {
     189           0 :         return None;
     190             :     }
     191           0 :     gpgme_sigsum_t sigsum = d->sigs[idx]->summary;
     192           0 :     unsigned int result = 0;
     193           0 :     if (sigsum & GPGME_SIGSUM_VALID) {
     194           0 :         result |= Valid;
     195             :     }
     196           0 :     if (sigsum & GPGME_SIGSUM_GREEN) {
     197           0 :         result |= Green;
     198             :     }
     199           0 :     if (sigsum & GPGME_SIGSUM_RED) {
     200           0 :         result |= Red;
     201             :     }
     202           0 :     if (sigsum & GPGME_SIGSUM_KEY_REVOKED) {
     203           0 :         result |= KeyRevoked;
     204             :     }
     205           0 :     if (sigsum & GPGME_SIGSUM_KEY_EXPIRED) {
     206           0 :         result |= KeyExpired;
     207             :     }
     208           0 :     if (sigsum & GPGME_SIGSUM_SIG_EXPIRED) {
     209           0 :         result |= SigExpired;
     210             :     }
     211           0 :     if (sigsum & GPGME_SIGSUM_KEY_MISSING) {
     212           0 :         result |= KeyMissing;
     213             :     }
     214           0 :     if (sigsum & GPGME_SIGSUM_CRL_MISSING) {
     215           0 :         result |= CrlMissing;
     216             :     }
     217           0 :     if (sigsum & GPGME_SIGSUM_CRL_TOO_OLD) {
     218           0 :         result |= CrlTooOld;
     219             :     }
     220           0 :     if (sigsum & GPGME_SIGSUM_BAD_POLICY) {
     221           0 :         result |= BadPolicy;
     222             :     }
     223           0 :     if (sigsum & GPGME_SIGSUM_SYS_ERROR) {
     224           0 :         result |= SysError;
     225             :     }
     226           0 :     if (sigsum & GPGME_SIGSUM_TOFU_CONFLICT) {
     227           0 :         result |= TofuConflict;
     228             :     }
     229           0 :     return static_cast<Summary>(result);
     230             : }
     231             : 
     232          10 : const char *GpgME::Signature::fingerprint() const
     233             : {
     234          10 :     return isNull() ? 0 : d->sigs[idx]->fpr ;
     235             : }
     236             : 
     237           0 : GpgME::Error GpgME::Signature::status() const
     238             : {
     239           0 :     return Error(isNull() ? 0 : d->sigs[idx]->status);
     240             : }
     241             : 
     242           0 : time_t GpgME::Signature::creationTime() const
     243             : {
     244           0 :     return static_cast<time_t>(isNull() ? 0 : d->sigs[idx]->timestamp);
     245             : }
     246             : 
     247           0 : time_t GpgME::Signature::expirationTime() const
     248             : {
     249           0 :     return static_cast<time_t>(isNull() ? 0 : d->sigs[idx]->exp_timestamp);
     250             : }
     251             : 
     252           0 : bool GpgME::Signature::neverExpires() const
     253             : {
     254           0 :     return expirationTime() == (time_t)0;
     255             : }
     256             : 
     257           0 : bool GpgME::Signature::isWrongKeyUsage() const
     258             : {
     259           0 :     return !isNull() && d->sigs[idx]->wrong_key_usage;
     260             : }
     261             : 
     262           0 : bool GpgME::Signature::isVerifiedUsingChainModel() const
     263             : {
     264           0 :     return !isNull() && d->sigs[idx]->chain_model;
     265             : }
     266             : 
     267           0 : GpgME::Signature::PKAStatus GpgME::Signature::pkaStatus() const
     268             : {
     269           0 :     if (!isNull()) {
     270           0 :         return static_cast<PKAStatus>(d->sigs[idx]->pka_trust);
     271             :     }
     272           0 :     return UnknownPKAStatus;
     273             : }
     274             : 
     275           0 : const char *GpgME::Signature::pkaAddress() const
     276             : {
     277           0 :     if (!isNull()) {
     278           0 :         return d->sigs[idx]->pka_address;
     279             :     }
     280           0 :     return 0;
     281             : }
     282             : 
     283           3 : GpgME::Signature::Validity GpgME::Signature::validity() const
     284             : {
     285           3 :     if (isNull()) {
     286           0 :         return Unknown;
     287             :     }
     288           3 :     switch (d->sigs[idx]->validity) {
     289             :     default:
     290           0 :     case GPGME_VALIDITY_UNKNOWN:   return Unknown;
     291           0 :     case GPGME_VALIDITY_UNDEFINED: return Undefined;
     292           0 :     case GPGME_VALIDITY_NEVER:     return Never;
     293           3 :     case GPGME_VALIDITY_MARGINAL:  return Marginal;
     294           0 :     case GPGME_VALIDITY_FULL:      return Full;
     295           0 :     case GPGME_VALIDITY_ULTIMATE:  return Ultimate;
     296             :     }
     297             : }
     298             : 
     299           0 : char GpgME::Signature::validityAsString() const
     300             : {
     301           0 :     if (isNull()) {
     302           0 :         return '?';
     303             :     }
     304           0 :     switch (d->sigs[idx]->validity) {
     305             :     default:
     306           0 :     case GPGME_VALIDITY_UNKNOWN:   return '?';
     307           0 :     case GPGME_VALIDITY_UNDEFINED: return 'q';
     308           0 :     case GPGME_VALIDITY_NEVER:     return 'n';
     309           0 :     case GPGME_VALIDITY_MARGINAL:  return 'm';
     310           0 :     case GPGME_VALIDITY_FULL:      return 'f';
     311           0 :     case GPGME_VALIDITY_ULTIMATE:  return 'u';
     312             :     }
     313             : }
     314             : 
     315           0 : GpgME::Error GpgME::Signature::nonValidityReason() const
     316             : {
     317           0 :     return Error(isNull() ? 0 : d->sigs[idx]->validity_reason);
     318             : }
     319             : 
     320           0 : unsigned int GpgME::Signature::publicKeyAlgorithm() const
     321             : {
     322           0 :     if (!isNull()) {
     323           0 :         return d->sigs[idx]->pubkey_algo;
     324             :     }
     325           0 :     return 0;
     326             : }
     327             : 
     328           0 : const char *GpgME::Signature::publicKeyAlgorithmAsString() const
     329             : {
     330           0 :     if (!isNull()) {
     331           0 :         return gpgme_pubkey_algo_name(d->sigs[idx]->pubkey_algo);
     332             :     }
     333           0 :     return 0;
     334             : }
     335             : 
     336           0 : unsigned int GpgME::Signature::hashAlgorithm() const
     337             : {
     338           0 :     if (!isNull()) {
     339           0 :         return d->sigs[idx]->hash_algo;
     340             :     }
     341           0 :     return 0;
     342             : }
     343             : 
     344           0 : const char *GpgME::Signature::hashAlgorithmAsString() const
     345             : {
     346           0 :     if (!isNull()) {
     347           0 :         return gpgme_hash_algo_name(d->sigs[idx]->hash_algo);
     348             :     }
     349           0 :     return 0;
     350             : }
     351             : 
     352           0 : const char *GpgME::Signature::policyURL() const
     353             : {
     354           0 :     return isNull() ? 0 : d->purls[idx] ;
     355             : }
     356             : 
     357           0 : GpgME::Notation GpgME::Signature::notation(unsigned int nidx) const
     358             : {
     359           0 :     return GpgME::Notation(d, idx, nidx);
     360             : }
     361             : 
     362           0 : std::vector<GpgME::Notation> GpgME::Signature::notations() const
     363             : {
     364           0 :     if (isNull()) {
     365           0 :         return std::vector<GpgME::Notation>();
     366             :     }
     367           0 :     std::vector<GpgME::Notation> result;
     368           0 :     result.reserve(d->nota[idx].size());
     369           0 :     for (unsigned int i = 0 ; i < d->nota[idx].size() ; ++i) {
     370           0 :         result.push_back(GpgME::Notation(d, idx, i));
     371             :     }
     372           0 :     return result;
     373             : }
     374             : 
     375          13 : GpgME::Key GpgME::Signature::key() const
     376             : {
     377          13 :     if (isNull()) {
     378           0 :         return Key();
     379             :     }
     380          13 :     return d->keys[idx];
     381             : }
     382             : 
     383             : class GpgME::Notation::Private
     384             : {
     385             : public:
     386             :     Private() : d(), sidx(0), nidx(0), nota(0) {}
     387           0 :     Private(const std::shared_ptr<VerificationResult::Private> &priv, unsigned int sindex, unsigned int nindex)
     388           0 :         : d(priv), sidx(sindex), nidx(nindex), nota(0)
     389             :     {
     390             : 
     391           0 :     }
     392           0 :     Private(gpgme_sig_notation_t n)
     393           0 :         : d(), sidx(0), nidx(0), nota(n ? new _gpgme_sig_notation(*n) : 0)
     394             :     {
     395           0 :         if (nota && nota->name) {
     396           0 :             nota->name = strdup(nota->name);
     397             :         }
     398           0 :         if (nota && nota->value) {
     399           0 :             nota->value = strdup(nota->value);
     400             :         }
     401           0 :     }
     402             :     Private(const Private &other)
     403             :         : d(other.d), sidx(other.sidx), nidx(other.nidx), nota(other.nota)
     404             :     {
     405             :         if (nota) {
     406             :             nota->name = strdup(nota->name);
     407             :             nota->value = strdup(nota->value);
     408             :         }
     409             :     }
     410           0 :     ~Private()
     411           0 :     {
     412           0 :         if (nota) {
     413           0 :             std::free(nota->name);  nota->name = 0;
     414           0 :             std::free(nota->value); nota->value = 0;
     415           0 :             delete nota;
     416             :         }
     417           0 :     }
     418             : 
     419             :     std::shared_ptr<VerificationResult::Private> d;
     420             :     unsigned int sidx, nidx;
     421             :     gpgme_sig_notation_t nota;
     422             : };
     423             : 
     424           0 : GpgME::Notation::Notation(const std::shared_ptr<VerificationResult::Private> &parent, unsigned int sindex, unsigned int nindex)
     425           0 :     : d(new Private(parent, sindex, nindex))
     426             : {
     427             : 
     428           0 : }
     429             : 
     430           0 : GpgME::Notation::Notation(gpgme_sig_notation_t nota)
     431           0 :     : d(new Private(nota))
     432             : {
     433             : 
     434           0 : }
     435             : 
     436           0 : GpgME::Notation::Notation() : d() {}
     437             : 
     438           0 : bool GpgME::Notation::isNull() const
     439             : {
     440           0 :     if (!d) {
     441           0 :         return true;
     442             :     }
     443           0 :     if (d->d) {
     444           0 :         return d->sidx >= d->d->nota.size() || d->nidx >= d->d->nota[d->sidx].size() ;
     445             :     }
     446           0 :     return !d->nota;
     447             : }
     448             : 
     449           0 : const char *GpgME::Notation::name() const
     450             : {
     451             :     return
     452           0 :         isNull() ? 0 :
     453           0 :         d->d ? d->d->nota[d->sidx][d->nidx].name :
     454           0 :         d->nota ? d->nota->name : 0 ;
     455             : }
     456             : 
     457           0 : const char *GpgME::Notation::value() const
     458             : {
     459             :     return
     460           0 :         isNull() ? 0 :
     461           0 :         d->d ? d->d->nota[d->sidx][d->nidx].value :
     462           0 :         d->nota ? d->nota->value : 0 ;
     463             : }
     464             : 
     465           0 : GpgME::Notation::Flags GpgME::Notation::flags() const
     466             : {
     467             :     return
     468             :         convert_from_gpgme_sig_notation_flags_t(
     469           0 :             isNull() ? 0 :
     470           0 :             d->d ? d->d->nota[d->sidx][d->nidx].flags :
     471           0 :             d->nota ? d->nota->flags : 0);
     472             : }
     473             : 
     474           0 : bool GpgME::Notation::isHumanReadable() const
     475             : {
     476           0 :     return flags() & HumanReadable;
     477             : }
     478             : 
     479           0 : bool GpgME::Notation::isCritical() const
     480             : {
     481           0 :     return flags() & Critical;
     482             : }
     483             : 
     484           0 : std::ostream &GpgME::operator<<(std::ostream &os, const VerificationResult &result)
     485             : {
     486           0 :     os << "GpgME::VerificationResult(";
     487           0 :     if (!result.isNull()) {
     488           0 :         os << "\n error:      " << result.error()
     489           0 :            << "\n fileName:   " << protect(result.fileName())
     490           0 :            << "\n signatures:\n";
     491           0 :         const std::vector<Signature> sigs = result.signatures();
     492             :         std::copy(sigs.begin(), sigs.end(),
     493           0 :                   std::ostream_iterator<Signature>(os, "\n"));
     494             :     }
     495           0 :     return os << ')';
     496             : }
     497             : 
     498           0 : std::ostream &GpgME::operator<<(std::ostream &os, Signature::PKAStatus pkaStatus)
     499             : {
     500             : #define OUTPUT( x ) if ( !(pkaStatus & (GpgME::Signature:: x)) ) {} else do { os << #x " "; } while(0)
     501           0 :     os << "GpgME::Signature::PKAStatus(";
     502             :     OUTPUT(UnknownPKAStatus);
     503           0 :     OUTPUT(PKAVerificationFailed);
     504           0 :     OUTPUT(PKAVerificationSucceeded);
     505             : #undef OUTPUT
     506           0 :     return os << ')';
     507             : }
     508             : 
     509           0 : std::ostream &GpgME::operator<<(std::ostream &os, Signature::Summary summary)
     510             : {
     511             : #define OUTPUT( x ) if ( !(summary & (GpgME::Signature:: x)) ) {} else do { os << #x " "; } while(0)
     512           0 :     os << "GpgME::Signature::Summary(";
     513           0 :     OUTPUT(Valid);
     514           0 :     OUTPUT(Green);
     515           0 :     OUTPUT(Red);
     516           0 :     OUTPUT(KeyRevoked);
     517           0 :     OUTPUT(KeyExpired);
     518           0 :     OUTPUT(SigExpired);
     519           0 :     OUTPUT(KeyMissing);
     520           0 :     OUTPUT(CrlMissing);
     521           0 :     OUTPUT(CrlTooOld);
     522           0 :     OUTPUT(BadPolicy);
     523           0 :     OUTPUT(SysError);
     524           0 :     OUTPUT(TofuConflict);
     525             : #undef OUTPUT
     526           0 :     return os << ')';
     527             : }
     528             : 
     529           0 : std::ostream &GpgME::operator<<(std::ostream &os, const Signature &sig)
     530             : {
     531           0 :     os << "GpgME::Signature(";
     532           0 :     if (!sig.isNull()) {
     533           0 :         os << "\n Summary:                   " << sig.summary()
     534           0 :            << "\n Fingerprint:               " << protect(sig.fingerprint())
     535           0 :            << "\n Status:                    " << sig.status()
     536           0 :            << "\n creationTime:              " << sig.creationTime()
     537           0 :            << "\n expirationTime:            " << sig.expirationTime()
     538           0 :            << "\n isWrongKeyUsage:           " << sig.isWrongKeyUsage()
     539           0 :            << "\n isVerifiedUsingChainModel: " << sig.isVerifiedUsingChainModel()
     540           0 :            << "\n pkaStatus:                 " << sig.pkaStatus()
     541           0 :            << "\n pkaAddress:                " << protect(sig.pkaAddress())
     542           0 :            << "\n validity:                  " << sig.validityAsString()
     543           0 :            << "\n nonValidityReason:         " << sig.nonValidityReason()
     544           0 :            << "\n publicKeyAlgorithm:        " << protect(sig.publicKeyAlgorithmAsString())
     545           0 :            << "\n hashAlgorithm:             " << protect(sig.hashAlgorithmAsString())
     546           0 :            << "\n policyURL:                 " << protect(sig.policyURL())
     547           0 :            << "\n notations:\n";
     548           0 :         const std::vector<Notation> nota = sig.notations();
     549             :         std::copy(nota.begin(), nota.end(),
     550           0 :                   std::ostream_iterator<Notation>(os, "\n"));
     551             :     }
     552           0 :     return os << ')';
     553             : }
     554             : 
     555           0 : std::ostream &GpgME::operator<<(std::ostream &os, Notation::Flags flags)
     556             : {
     557           0 :     os << "GpgME::Notation::Flags(";
     558             : #define OUTPUT( x ) if ( !(flags & (GpgME::Notation:: x)) ) {} else do { os << #x " "; } while(0)
     559           0 :     OUTPUT(HumanReadable);
     560           0 :     OUTPUT(Critical);
     561             : #undef OUTPUT
     562           0 :     return os << ')';
     563             : }
     564             : 
     565           0 : std::ostream &GpgME::operator<<(std::ostream &os, const Notation &nota)
     566             : {
     567           0 :     os << "GpgME::Signature::Notation(";
     568           0 :     if (!nota.isNull()) {
     569           0 :         os << "\n name:  " << protect(nota.name())
     570           0 :            << "\n value: " << protect(nota.value())
     571           0 :            << "\n flags: " << nota.flags()
     572           0 :            << '\n';
     573             :     }
     574           0 :     return os << ")";
     575          18 : }

Generated by: LCOV version 1.11