~ruther/guix-local

3af52f845fe2ceb448416ac7b9f48925673c594e — Congcong Kuo 7 months ago cbda925
daemon: Bump to C++20 and use ‘std::format’ instead of ‘boost::format’.

* nix/boost: This directory and all files inside it are removed.
* nix/libstore/build.cc (Goal::trace): Use ‘std::string’ instead of ‘const format &’.
(DerivationGoal::startBuilder, ...): Use ‘std::format’ or ‘std::vformat’ instead of ‘boost::format’.
* nix/libstore/builtins.cc (builtinDownload): Same.
* nix/libstore/derivations.cc (DerivationOutput::parseHashInfo, ...): Same.
* nix/libstore/gc.cc (LocalStore::openGCLock, ...): Same.
* nix/libstore/globals.cc (Settings::_get): Same.
* nix/libstore/local-store.cc: (checkStoreNotSymlink, ...): Same.
* nix/libstore/misc.cc (dfsVisit, showBytes): Same
* nix/libstore/optimise-store.cc (makeWritable, ...): Same.
* nix/libstore/pathlocks.cc (openLockFile, ...): Same.
* nix/libstore/references.cc (search, scanForReferences): Same.
* nix/libstore/sqlite.hh (throwSQLiteError): Use ‘std::string’ instead of ‘const format &’.
* nix/libstore/sqlite.cc (throwSQLiteError): Use ‘std::string’ instead of ‘const format &’.
* nix/libstore/store-api.cc (assertStorePath, ...): Use ‘std::format’ instead of ‘boost::format’.
* nix/libutil/affinity.cc (setAffinityTo): Same.
* nix/libutil/archive.cc (dumpContents, ...): Same.
* nix/libutil/hash.cc (parseHash, parseHash32, parseHash16or32, hashFile): Same.
* nix/libutil/hash.hh (parseHash, parseHash32, parseHash16or32, isHash): Same.
* nix/libutil/serialise.cc : Add ‘<cassert>’ header file.
* nix/libutil/spawn.cc (addPhaseAfter, ...): Use ‘std::format’ instead of ‘boost::format’.
* nix/libutil/types.hh (FormatOrString): Removed.
(BaseError, BaseError::addPrefix, SysError, MakeError):
Use ‘std::string or std::string_view’ instead of ‘FormatOrString’.
* nix/libutil/util.hh (Nest::open, printMsg_, warnOnce, expect): Same.
* nix/libutil/util.cc (BaseError::BaseError, ...): Same.
(writeToStderr, _interrupted): Use std::uncaught_exceptions() instead of std::uncaught_exception()
* nix/nix-daemon/nix-daemon.cc (performOp, ...): Same.
* nix/nix-daemon/guix-daemon.cc (string_to_bool, ...): Same.
* nix/local.mk: Remove ‘libformat.a’ from ‘noinst_LIBRARIES’,
remove ‘libformat_a_SOURCES’ and ‘libformat_headers’,
remove ‘libformat_a_CPPFLAGS’ from ‘libutil_a_CPPFLAGS’ and ‘guix_daemon_LDADD’,
update ‘AM_CXXFLAGS’ to ‘-std=c++20’.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
40 files changed, 645 insertions(+), 3077 deletions(-)

D nix/boost/assert.hpp
D nix/boost/format.hpp
D nix/boost/format/exceptions.hpp
D nix/boost/format/feed_args.hpp
D nix/boost/format/format_class.hpp
D nix/boost/format/format_fwd.hpp
D nix/boost/format/format_implementation.cc
D nix/boost/format/free_funcs.cc
D nix/boost/format/group.hpp
D nix/boost/format/internals.hpp
D nix/boost/format/internals_fwd.hpp
D nix/boost/format/macros_default.hpp
D nix/boost/format/parsing.cc
D nix/boost/throw_exception.hpp
M nix/libstore/build.cc
M nix/libstore/builtins.cc
M nix/libstore/builtins.hh
M nix/libstore/derivations.cc
M nix/libstore/gc.cc
M nix/libstore/globals.cc
M nix/libstore/local-store.cc
M nix/libstore/misc.cc
M nix/libstore/optimise-store.cc
M nix/libstore/pathlocks.cc
M nix/libstore/references.cc
M nix/libstore/sqlite.cc
M nix/libstore/sqlite.hh
M nix/libstore/store-api.cc
M nix/libutil/affinity.cc
M nix/libutil/archive.cc
M nix/libutil/hash.cc
M nix/libutil/hash.hh
M nix/libutil/serialise.cc
M nix/libutil/spawn.cc
M nix/libutil/types.hh
M nix/libutil/util.cc
M nix/libutil/util.hh
M nix/local.mk
M nix/nix-daemon/guix-daemon.cc
M nix/nix-daemon/nix-daemon.cc
D nix/boost/assert.hpp => nix/boost/assert.hpp +0 -38
@@ 1,38 0,0 @@
//
//  boost/assert.hpp - BOOST_ASSERT(expr)
//
//  Copyright (c) 2001, 2002 Peter Dimov and Multi Media Ltd.
//
//  Permission to copy, use, modify, sell and distribute this software
//  is granted provided this copyright notice appears in all copies.
//  This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.
//
//  Note: There are no include guards. This is intentional.
//
//  See http://www.boost.org/libs/utility/assert.html for documentation.
//

#undef BOOST_ASSERT

#if defined(BOOST_DISABLE_ASSERTS)

# define BOOST_ASSERT(expr) ((void)0)

#elif defined(BOOST_ENABLE_ASSERT_HANDLER)

#include <boost/current_function.hpp>

namespace boost
{

void assertion_failed(char const * expr, char const * function, char const * file, long line); // user defined

} // namespace boost

#define BOOST_ASSERT(expr) ((expr)? ((void)0): ::boost::assertion_failed(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__))

#else
# include <assert.h>
# define BOOST_ASSERT(expr) assert(expr)
#endif

D nix/boost/format.hpp => nix/boost/format.hpp +0 -64
@@ 1,64 0,0 @@
// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream

// ----------------------------------------------------------------------------
// format.hpp :  primary header
// ----------------------------------------------------------------------------

#ifndef BOOST_FORMAT_HPP
#define BOOST_FORMAT_HPP

#include <vector>
#include <string>
#include <sstream>
#include <cassert>

#if HAVE_LOCALE
#include <locale>
#else
#define BOOST_NO_STD_LOCALE
#define BOOST_NO_LOCALE_ISIDIGIT
#include <cctype>
#endif

#include <boost/format/macros_default.hpp>


// ****  Forward declarations ----------------------------------
#include <boost/format/format_fwd.hpp>           // basic_format<Ch,Tr>, and other frontends
#include <boost/format/internals_fwd.hpp>        // misc forward declarations for internal use


// ****  Auxiliary structs (stream_format_state<Ch,Tr> , and format_item<Ch,Tr> )
#include <boost/format/internals.hpp>    

// ****  Format  class  interface --------------------------------
#include <boost/format/format_class.hpp>

// **** Exceptions -----------------------------------------------
#include <boost/format/exceptions.hpp>

// **** Implementation -------------------------------------------
//#include <boost/format/format_implementation.hpp>   // member functions

#include <boost/format/group.hpp>                   // class for grouping arguments

#include <boost/format/feed_args.hpp>               // argument-feeding functions
//#include <boost/format/parsing.hpp>                 // format-string parsing (member-)functions

// **** Implementation of the free functions ----------------------
//#include <boost/format/free_funcs.hpp>


#endif // BOOST_FORMAT_HPP

D nix/boost/format/exceptions.hpp => nix/boost/format/exceptions.hpp +0 -96
@@ 1,96 0,0 @@
// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)

// ------------------------------------------------------------------------------
// exceptions.hpp 
// ------------------------------------------------------------------------------


#ifndef BOOST_FORMAT_EXCEPTIONS_HPP
#define BOOST_FORMAT_EXCEPTIONS_HPP


#include <stdexcept>


namespace boost {

namespace io {

// **** exceptions -----------------------------------------------

class format_error : public std::exception
{
public:
  format_error() {}
  virtual const char *what() const throw()
  {
    return "boost::format_error: "
      "format generic failure";
  }
};

class bad_format_string : public format_error
{
public:
  bad_format_string() {}
  virtual const char *what() const throw()
  {
    return "boost::bad_format_string: "
      "format-string is ill-formed";
  }
};

class too_few_args : public format_error
{
public:
  too_few_args() {}
  virtual const char *what() const throw()
  {
    return "boost::too_few_args: "
      "format-string referred to more arguments than were passed";
  }
};

class too_many_args : public format_error
{
public:
  too_many_args() {}
  virtual const char *what() const throw()
  {
    return "boost::too_many_args: "
      "format-string referred to less arguments than were passed";
  }
};


class  out_of_range : public format_error
{
public:
  out_of_range() {}
  virtual const char *what() const throw()
  {
    return "boost::out_of_range: "
      "tried to refer to an argument (or item) number which is out of range, "
      "according to the format string.";
  }
};


} // namespace io

} // namespace boost


#endif // BOOST_FORMAT_EXCEPTIONS_HPP

D nix/boost/format/feed_args.hpp => nix/boost/format/feed_args.hpp +0 -247
@@ 1,247 0,0 @@
// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream

// ----------------------------------------------------------------------------
// feed_args.hpp :  functions for processing each argument 
//                      (feed, feed_manip, and distribute)
// ----------------------------------------------------------------------------


#ifndef BOOST_FORMAT_FEED_ARGS_HPP
#define BOOST_FORMAT_FEED_ARGS_HPP

#include "boost/format/format_class.hpp"
#include "boost/format/group.hpp"

#include "boost/throw_exception.hpp"

namespace boost {
namespace io {
namespace detail {
namespace  { 

  inline
  void empty_buf(BOOST_IO_STD ostringstream & os) { 
    static const std::string emptyStr;
    os.str(emptyStr); 
  }

  void do_pad( std::string & s, 
                std::streamsize w, 
                const char c, 
                std::ios::fmtflags f, 
                bool center) 
    // applies centered / left / right  padding  to the string s.
    // Effects : string s is padded.
  {
    std::streamsize n=w-s.size();
    if(n<=0) {
      return;
    }
    if(center) 
      {
        s.reserve(w); // allocate once for the 2 inserts
        const std::streamsize n1 = n /2, n0 = n - n1; 
        s.insert(s.begin(), n0, c);
        s.append(n1, c);
      } 
    else 
      {
        if(f & std::ios::left) {
          s.append(n, c);
        }
        else {
          s.insert(s.begin(), n, c);
        }
      }
  } // -do_pad(..) 


  template<class T> inline
  void put_head(BOOST_IO_STD ostream& , const T& ) {
  }

  template<class T> inline
  void put_head( BOOST_IO_STD ostream& os, const group1<T>& x ) {
    os << group_head(x.a1_); // send the first N-1 items, not the last
  }

  template<class T> inline
  void put_last( BOOST_IO_STD ostream& os, const T& x ) {
    os << x ;
  }

  template<class T> inline
  void put_last( BOOST_IO_STD ostream& os, const group1<T>& x ) {
    os << group_last(x.a1_); // this selects the last element
  }

#ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST 
  template<class T> inline
  void put_head( BOOST_IO_STD ostream& , T& ) {
  }

  template<class T> inline
  void put_last( BOOST_IO_STD ostream& os, T& x ) {
    os << x ;
  }
#endif



  
template<class T> 
void put( T x, 
          const format_item& specs, 
          std::string & res, 
          BOOST_IO_STD ostringstream& oss_ )
{
  // does the actual conversion of x, with given params, into a string
  // using the *supplied* strinstream. (the stream state is important)

  typedef std::string string_t;
  typedef format_item  format_item_t;

  stream_format_state   prev_state(oss_);
    
  specs.state_.apply_on(oss_);

  // in case x is a group, apply the manip part of it, 
  // in order to find width
  put_head( oss_, x );
  empty_buf( oss_);

  const std::streamsize w=oss_.width();
  const std::ios::fmtflags fl=oss_.flags();
  const bool internal = (fl & std::ios::internal) != 0;
  const bool two_stepped_padding = internal
    &&  ! ( specs.pad_scheme_ & format_item_t::spacepad ) 
    && specs.truncate_ < 0 ;
      

  if(! two_stepped_padding) 
    {
      if(w>0) // handle simple padding via do_pad, not natively in stream 
        oss_.width(0);
      put_last( oss_, x);
      res = oss_.str();

      if (specs.truncate_ >= 0)
        res.erase(specs.truncate_);

      // complex pads :
      if(specs.pad_scheme_ & format_item_t::spacepad)
        {
          if( res.size()==0 ||   ( res[0]!='+' && res[0]!='-'  ))
            {
              res.insert(res.begin(), 1, ' '); // insert 1 space at  pos 0
            }
        }
      if(w > 0) // need do_pad
        {
          do_pad(res,w,oss_.fill(), fl, (specs.pad_scheme_ & format_item_t::centered) !=0 );
        }
    } 
  else  // 2-stepped padding
    {
      put_last( oss_, x); // oss_.width() may result in padding.
      res = oss_.str();
      
      if (specs.truncate_ >= 0)
        res.erase(specs.truncate_);

      if( res.size() - w > 0)
        { //   length w exceeded
          // either it was multi-output with first output padding up all width..
          // either it was one big arg and we are fine.
          empty_buf( oss_);
          oss_.width(0);
          put_last(oss_, x );
          string_t tmp = oss_.str();  // minimal-length output
          std::streamsize d;
          if( (d=w - tmp.size()) <=0 ) 
            {
              // minimal length is already >= w, so no padding  (cool!)
              res.swap(tmp);
            }
          else
            { // hum..  we need to pad (it was necessarily multi-output)
              typedef typename string_t::size_type size_type;
              size_type i = 0;
              while( i<tmp.size() && tmp[i] == res[i] ) // find where we should pad.
                ++i;
              tmp.insert(i, static_cast<size_type>( d ), oss_.fill());
              res.swap( tmp );
            }
        }
      else 
        { // okay, only one thing was printed and padded, so res is fine.
        }
    }

  prev_state.apply_on(oss_);
  empty_buf( oss_);
  oss_.clear();
} // end- put(..)


}  // local namespace





template<class T> 
void distribute(basic_format& self, T x) 
  // call put(x, ..) on every occurence of the current argument :
{
  if(self.cur_arg_ >= self.num_args_)
    {
      if( self.exceptions() & too_many_args_bit )
        boost::throw_exception(too_many_args()); // too many variables have been supplied !
      else return;
    }
  for(unsigned long i=0; i < self.items_.size(); ++i)
    {
      if(self.items_[i].argN_ == self.cur_arg_)
        {
          put<T> (x, self.items_[i], self.items_[i].res_, self.oss_ );
        }
    }
}

template<class T> 
basic_format&  feed(basic_format& self, T x) 
{
  if(self.dumped_) self.clear();
  distribute<T> (self, x);
  ++self.cur_arg_;
  if(self.bound_.size() != 0)
    {
      while( self.cur_arg_ < self.num_args_ && self.bound_[self.cur_arg_] )
        ++self.cur_arg_;
    }

  // this arg is finished, reset the stream's format state
  self.state0_.apply_on(self.oss_);
  return self;
}
    

} // namespace detail
} // namespace io
} // namespace boost


#endif //  BOOST_FORMAT_FEED_ARGS_HPP

D nix/boost/format/format_class.hpp => nix/boost/format/format_class.hpp +0 -135
@@ 1,135 0,0 @@
// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)

// ------------------------------------------------------------------------------
// format_class.hpp :  class interface
// ------------------------------------------------------------------------------


#ifndef BOOST_FORMAT_CLASS_HPP
#define BOOST_FORMAT_CLASS_HPP

#include <vector>
#include <string>

#include <boost/format/format_fwd.hpp>
#include <boost/format/internals_fwd.hpp>

#include <boost/format/internals.hpp>

namespace boost {

class basic_format 
{
public:
  typedef std::string                string_t;
  typedef BOOST_IO_STD ostringstream internal_stream_t;
private:
  typedef BOOST_IO_STD ostream       stream_t;
  typedef io::detail::stream_format_state  stream_format_state;
  typedef io::detail::format_item          format_item_t;

public:
  basic_format(const char* str);
  basic_format(const string_t& s);
#ifndef BOOST_NO_STD_LOCALE
  basic_format(const char* str, const std::locale & loc);
  basic_format(const string_t& s, const std::locale & loc);
#endif // no locale
  basic_format(const basic_format& x);
  basic_format& operator= (const basic_format& x);

  basic_format& clear(); // empty the string buffers (except bound arguments, see clear_binds() )

  // pass arguments through those operators :
  template<class T>  basic_format&   operator%(const T& x) 
  { 
    return io::detail::feed<const T&>(*this,x);
  }

#ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST
  template<class T>  basic_format&   operator%(T& x) 
  {
    return io::detail::feed<T&>(*this,x);
  }
#endif


  // system for binding arguments :
  template<class T>  
  basic_format&         bind_arg(int argN, const T& val) 
  {
    return io::detail::bind_arg_body(*this, argN, val); 
  }
  basic_format&         clear_bind(int argN);
  basic_format&         clear_binds();

  // modify the params of a directive, by applying a manipulator :
  template<class T> 
  basic_format&  modify_item(int itemN, const T& manipulator) 
  {
    return io::detail::modify_item_body(*this, itemN, manipulator) ;
  }

  // Choosing which errors will throw exceptions :
  unsigned char exceptions() const;
  unsigned char exceptions(unsigned char newexcept);

  // final output
  string_t str() const;
  friend BOOST_IO_STD ostream& 
  operator<< ( BOOST_IO_STD ostream& , const basic_format& ); 
                      

  template<class T>  friend basic_format&  
  io::detail::feed(basic_format&, T);
    
  template<class T>  friend   
  void io::detail::distribute(basic_format&, T);
  
  template<class T>  friend
  basic_format&  io::detail::modify_item_body(basic_format&, int, const T&);

  template<class T> friend
  basic_format&  io::detail::bind_arg_body(basic_format&, int, const T&);

// make the members private only if the friend templates are supported
private:

  // flag bits, used for style_
  enum style_values  { ordered = 1,        // set only if all directives are  positional directives
                       special_needs = 4 };     

  // parse the format string :
  void parse(const string_t&);

  int                           style_;         // style of format-string :  positional or not, etc
  int                           cur_arg_;       // keep track of wich argument will come
  int                           num_args_;      // number of expected arguments
  mutable bool                  dumped_;        // true only after call to str() or <<
  std::vector<format_item_t>    items_;         // vector of directives (aka items)
  string_t                      prefix_;        // piece of string to insert before first item

  std::vector<bool>             bound_;         // stores which arguments were bound
                                                //   size = num_args OR zero
  internal_stream_t             oss_;           // the internal stream.
  stream_format_state           state0_;        // reference state for oss_
  unsigned char                 exceptions_;
}; // class basic_format


} // namespace boost


#endif // BOOST_FORMAT_CLASS_HPP

D nix/boost/format/format_fwd.hpp => nix/boost/format/format_fwd.hpp +0 -49
@@ 1,49 0,0 @@
// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)

// ------------------------------------------------------------------------------
// format_fwd.hpp :  forward declarations, for primary header format.hpp
// ------------------------------------------------------------------------------

#ifndef BOOST_FORMAT_FWD_HPP
#define BOOST_FORMAT_FWD_HPP

#include <string>
#include <iosfwd>

namespace boost {

class basic_format;

typedef basic_format    format;

namespace io {
enum format_error_bits { bad_format_string_bit = 1, 
                         too_few_args_bit = 2, too_many_args_bit = 4,
                         out_of_range_bit = 8,
                         all_error_bits = 255, no_error_bits=0 };
                  
// Convertion:  format   to   string
std::string     str(const basic_format& ) ;

} // namespace io


BOOST_IO_STD ostream& 
operator<<( BOOST_IO_STD ostream&, const basic_format&);


} // namespace boost

#endif // BOOST_FORMAT_FWD_HPP

D nix/boost/format/format_implementation.cc => nix/boost/format/format_implementation.cc +0 -256
@@ 1,256 0,0 @@
// -*- C++ -*-
//  Boost general library format ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream

// ----------------------------------------------------------------------------
// format_implementation.hpp  Implementation of the basic_format class
// ----------------------------------------------------------------------------


#ifndef BOOST_FORMAT_IMPLEMENTATION_HPP
#define BOOST_FORMAT_IMPLEMENTATION_HPP

#include <boost/throw_exception.hpp>
#include <boost/assert.hpp>
#include <boost/format.hpp>

namespace boost {

// --------  format:: -------------------------------------------
basic_format::basic_format(const char* str)
    : style_(0), cur_arg_(0), num_args_(0), dumped_(false),
      items_(), oss_(), exceptions_(io::all_error_bits)
{
    state0_.set_by_stream(oss_);
    string_t emptyStr;
    if( !str) str = emptyStr.c_str();
    parse( str );
}

#ifndef BOOST_NO_STD_LOCALE
basic_format::basic_format(const char* str, const std::locale & loc)
    : style_(0), cur_arg_(0), num_args_(0), dumped_(false),
      items_(), oss_(), exceptions_(io::all_error_bits)
{
    oss_.imbue( loc );
    state0_.set_by_stream(oss_);
    string_t emptyStr;
    if( !str) str = emptyStr.c_str();
    parse( str );
}

basic_format::basic_format(const string_t& s, const std::locale & loc)
    : style_(0), cur_arg_(0), num_args_(0), dumped_(false),
      items_(),  oss_(), exceptions_(io::all_error_bits)
{
    oss_.imbue( loc );
    state0_.set_by_stream(oss_);
    parse(s);  
}
#endif //BOOST_NO_STD_LOCALE

basic_format::basic_format(const string_t& s)
    : style_(0), cur_arg_(0), num_args_(0), dumped_(false),
      items_(),  oss_(), exceptions_(io::all_error_bits)
{
    state0_.set_by_stream(oss_);
    parse(s);  
}

basic_format:: basic_format(const basic_format& x)
    : style_(x.style_), cur_arg_(x.cur_arg_), num_args_(x.num_args_), dumped_(false), 
      items_(x.items_), prefix_(x.prefix_), bound_(x.bound_), 
      oss_(),   // <- we obviously can't copy x.oss_
      state0_(x.state0_), exceptions_(x.exceptions_)
{ 
    state0_.apply_on(oss_);
} 

basic_format& basic_format::operator= (const basic_format& x)
{
    if(this == &x)
      return *this;
    state0_ = x.state0_;
    state0_.apply_on(oss_);

    // plus all the other (trivial) assignments :
    exceptions_ = x.exceptions_;
    items_ = x.items_;
    prefix_ = x.prefix_;
    bound_=x.bound_;
    style_=x.style_; 
    cur_arg_=x.cur_arg_; 
    num_args_=x.num_args_;
    dumped_=x.dumped_;
    return *this;
}


unsigned char basic_format::exceptions() const 
{
  return exceptions_; 
}

unsigned char basic_format::exceptions(unsigned char newexcept) 
{ 
  unsigned char swp = exceptions_; 
  exceptions_ = newexcept; 
  return swp; 
}


basic_format& basic_format ::clear()
  // empty the string buffers (except bound arguments, see clear_binds() )
  // and make the format object ready for formatting a new set of arguments
{
    BOOST_ASSERT( bound_.size()==0 || num_args_ == static_cast<int>(bound_.size()) );

    for(unsigned long i=0; i<items_.size(); ++i){
      items_[i].state_ = items_[i].ref_state_;
      // clear converted strings only if the corresponding argument is not  bound :
      if( bound_.size()==0 || !bound_[ items_[i].argN_ ] )  items_[i].res_.resize(0);
    }
    cur_arg_=0; dumped_=false;
    // maybe first arg is bound:
    if(bound_.size() != 0)
      {
        while(cur_arg_ < num_args_ && bound_[cur_arg_] )      ++cur_arg_;
      }
    return *this;
}

basic_format& basic_format ::clear_binds() 
  // cancel all bindings, and clear()
{
    bound_.resize(0);
    clear();
    return *this;
}

basic_format& basic_format::clear_bind(int argN) 
  // cancel the binding of ONE argument, and clear()
{
    if(argN<1 || argN > num_args_ || bound_.size()==0 || !bound_[argN-1] ) 
      {
        if( exceptions() & io::out_of_range_bit )
          boost::throw_exception(io::out_of_range()); // arg not in range.
        else return *this;
      }
    bound_[argN-1]=false;
    clear();
    return *this;
}



std::string basic_format::str() const
{
  dumped_=true;
  if(items_.size()==0)
    return prefix_;
  if( cur_arg_ < num_args_)
      if( exceptions() & io::too_few_args_bit )
        boost::throw_exception(io::too_few_args()); // not enough variables have been supplied !

  unsigned long sz = prefix_.size();
  unsigned long i;
  for(i=0; i < items_.size(); ++i) 
    sz += items_[i].res_.size() + items_[i].appendix_.size();
  string_t res;
  res.reserve(sz);

  res += prefix_;
  for(i=0; i < items_.size(); ++i) 
  {
    const format_item_t& item = items_[i];
    res += item.res_;
    if( item.argN_ == format_item_t::argN_tabulation) 
    { 
      BOOST_ASSERT( item.pad_scheme_ & format_item_t::tabulation);
      std::streamsize  n = item.state_.width_ - res.size();
      if( n > 0 )
        res.append( n, item.state_.fill_ );
    }
    res += item.appendix_;
  }
  return res;
}

namespace io {
namespace detail {

template<class T>
basic_format&  bind_arg_body( basic_format& self, 
                                      int argN, 
                                      const T& val)
  // bind one argument to a fixed value
  // this is persistent over clear() calls, thus also over str() and <<
{
    if(self.dumped_) self.clear(); // needed, because we will modify cur_arg_..
    if(argN<1 || argN > self.num_args_) 
      {
        if( self.exceptions() & io::out_of_range_bit )
          boost::throw_exception(io::out_of_range()); // arg not in range.
        else return self;
      }
    if(self.bound_.size()==0) 
      self.bound_.assign(self.num_args_,false);
    else 
      BOOST_ASSERT( self.num_args_ == static_cast<signed int>(self.bound_.size()) );
    int o_cur_arg = self.cur_arg_;
    self.cur_arg_ = argN-1; // arrays begin at 0

    self.bound_[self.cur_arg_]=false; // if already set, we unset and re-sets..
    self.operator%(val); // put val at the right place, because cur_arg is set
    

    // Now re-position cur_arg before leaving :
    self.cur_arg_ = o_cur_arg; 
    self.bound_[argN-1]=true;
    if(self.cur_arg_ == argN-1 )
      // hum, now this arg is bound, so move to next free arg
      {
        while(self.cur_arg_ < self.num_args_ && self.bound_[self.cur_arg_])   ++self.cur_arg_;
      }
    // In any case, we either have all args, or are on a non-binded arg :
    BOOST_ASSERT( self.cur_arg_ >= self.num_args_ || ! self.bound_[self.cur_arg_]);
    return self;
}

template<class T>
basic_format&  modify_item_body( basic_format& self,
                                      int itemN, 
                                      const T& manipulator)
  // applies a manipulator to the format_item describing a given directive.
  // this is a permanent change, clear or clear_binds won't cancel that.
{
  if(itemN<1 || itemN >= static_cast<signed int>(self.items_.size() )) 
    {
      if( self.exceptions() & io::out_of_range_bit ) 
        boost::throw_exception(io::out_of_range()); // item not in range.
      else return self;
    }
  self.items_[itemN-1].ref_state_.apply_manip( manipulator );
  self.items_[itemN-1].state_ = self.items_[itemN-1].ref_state_;
  return self;
}

} // namespace detail

} // namespace io

} // namespace boost



#endif  // BOOST_FORMAT_IMPLEMENTATION_HPP

D nix/boost/format/free_funcs.cc => nix/boost/format/free_funcs.cc +0 -71
@@ 1,71 0,0 @@
// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)

// ------------------------------------------------------------------------------
// free_funcs.hpp :  implementation of the free functions declared in namespace format
// ------------------------------------------------------------------------------

#ifndef BOOST_FORMAT_FUNCS_HPP
#define BOOST_FORMAT_FUNCS_HPP

#include "boost/format.hpp"
#include "boost/throw_exception.hpp"

namespace boost {

namespace io {
  inline 
  std::string str(const basic_format& f) 
    // adds up all pieces of strings and converted items, and return the formatted string
  {
    return f.str();
  }
}   // - namespace io

BOOST_IO_STD ostream& 
operator<<( BOOST_IO_STD ostream& os, 
            const boost::basic_format& f) 
  // effect: "return os << str(f);" but we can try to do it faster
{
  typedef boost::basic_format   format_t;
  if(f.items_.size()==0) 
    os << f.prefix_;
  else {
    if(f.cur_arg_ < f.num_args_)
      if( f.exceptions() & io::too_few_args_bit )
        boost::throw_exception(io::too_few_args()); // not enough variables have been supplied !
    if(f.style_ & format_t::special_needs) 
        os << f.str();
    else {
    // else we dont have to count chars output, so we dump directly to os :
      os << f.prefix_;
      for(unsigned long i=0; i<f.items_.size(); ++i) 
        {
          const format_t::format_item_t& item = f.items_[i];
          os << item.res_;
          os << item.appendix_;

        }
    }
  }
  f.dumped_=true;
  return os;
}



} // namespace boost


#endif // BOOST_FORMAT_FUNCS_HPP

D nix/boost/format/group.hpp => nix/boost/format/group.hpp +0 -680
@@ 1,680 0,0 @@

// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream

// ----------------------------------------------------------------------------

// group.hpp :  encapsulates a group of manipulators along with an argument
//                      
// group_head : cut the last element of a group out.
// (is overloaded below on each type of group)

// group_last : returns the last element of a group
// (is overloaded below on each type of group)

// ----------------------------------------------------------------------------


#ifndef BOOST_FORMAT_GROUP_HPP
#define BOOST_FORMAT_GROUP_HPP


namespace boost {
namespace io {


namespace detail {


// empty group, but useful even though.
struct group0 
{
    group0()      {}
};

template <class Ch, class Tr>
inline
BOOST_IO_STD ostream&
operator << ( BOOST_IO_STD ostream& os,
             const group0& )
{ 
   return os; 
}

template <class T1>
struct group1
{
    T1 a1_;
    group1(T1 a1)
      : a1_(a1)
      {}
};

template <class Ch, class Tr, class T1>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
             const group1<T1>& x)
{ 
   os << x.a1_;  
   return os; 
}




template <class T1,class T2>
struct group2
{
    T1 a1_;
    T2 a2_;
    group2(T1 a1,T2 a2)
      : a1_(a1),a2_(a2)
      {}
};

template <class Ch, class Tr, class T1,class T2>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
             const group2<T1,T2>& x)
{ 
   os << x.a1_<< x.a2_;  
   return os; 
}

template <class T1,class T2,class T3>
struct group3
{
    T1 a1_;
    T2 a2_;
    T3 a3_;
    group3(T1 a1,T2 a2,T3 a3)
      : a1_(a1),a2_(a2),a3_(a3)
      {}
};

template <class Ch, class Tr, class T1,class T2,class T3>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
             const group3<T1,T2,T3>& x)
{ 
   os << x.a1_<< x.a2_<< x.a3_;  
   return os; 
}

template <class T1,class T2,class T3,class T4>
struct group4
{
    T1 a1_;
    T2 a2_;
    T3 a3_;
    T4 a4_;
    group4(T1 a1,T2 a2,T3 a3,T4 a4)
      : a1_(a1),a2_(a2),a3_(a3),a4_(a4)
      {}
};

template <class Ch, class Tr, class T1,class T2,class T3,class T4>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
             const group4<T1,T2,T3,T4>& x)
{ 
   os << x.a1_<< x.a2_<< x.a3_<< x.a4_;  
   return os; 
}

template <class T1,class T2,class T3,class T4,class T5>
struct group5
{
    T1 a1_;
    T2 a2_;
    T3 a3_;
    T4 a4_;
    T5 a5_;
    group5(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5)
      : a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5)
      {}
};

template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
             const group5<T1,T2,T3,T4,T5>& x)
{ 
   os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_;  
   return os; 
}

template <class T1,class T2,class T3,class T4,class T5,class T6>
struct group6
{
    T1 a1_;
    T2 a2_;
    T3 a3_;
    T4 a4_;
    T5 a5_;
    T6 a6_;
    group6(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6)
      : a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5),a6_(a6)
      {}
};

template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5,class T6>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
             const group6<T1,T2,T3,T4,T5,T6>& x)
{ 
   os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_<< x.a6_;  
   return os; 
}

template <class T1,class T2,class T3,class T4,class T5,class T6,class T7>
struct group7
{
    T1 a1_;
    T2 a2_;
    T3 a3_;
    T4 a4_;
    T5 a5_;
    T6 a6_;
    T7 a7_;
    group7(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7)
      : a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5),a6_(a6),a7_(a7)
      {}
};

template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5,class T6,class T7>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
             const group7<T1,T2,T3,T4,T5,T6,T7>& x)
{ 
   os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_<< x.a6_<< x.a7_;  
   return os; 
}

template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8>
struct group8
{
    T1 a1_;
    T2 a2_;
    T3 a3_;
    T4 a4_;
    T5 a5_;
    T6 a6_;
    T7 a7_;
    T8 a8_;
    group8(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8)
      : a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5),a6_(a6),a7_(a7),a8_(a8)
      {}
};

template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
             const group8<T1,T2,T3,T4,T5,T6,T7,T8>& x)
{ 
   os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_<< x.a6_<< x.a7_<< x.a8_;  
   return os; 
}

template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9>
struct group9
{
    T1 a1_;
    T2 a2_;
    T3 a3_;
    T4 a4_;
    T5 a5_;
    T6 a6_;
    T7 a7_;
    T8 a8_;
    T9 a9_;
    group9(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8,T9 a9)
      : a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5),a6_(a6),a7_(a7),a8_(a8),a9_(a9)
      {}
};

template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
             const group9<T1,T2,T3,T4,T5,T6,T7,T8,T9>& x)
{ 
   os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_<< x.a6_<< x.a7_<< x.a8_<< x.a9_;  
   return os; 
}

template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9,class T10>
struct group10
{
    T1 a1_;
    T2 a2_;
    T3 a3_;
    T4 a4_;
    T5 a5_;
    T6 a6_;
    T7 a7_;
    T8 a8_;
    T9 a9_;
    T10 a10_;
    group10(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8,T9 a9,T10 a10)
      : a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5),a6_(a6),a7_(a7),a8_(a8),a9_(a9),a10_(a10)
      {}
};

template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9,class T10>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
             const group10<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10>& x)
{ 
   os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_<< x.a6_<< x.a7_<< x.a8_<< x.a9_<< x.a10_;  
   return os; 
}




template <class T1,class T2>
inline
group1<T1> 
group_head( group2<T1,T2> const& x)
{
   return group1<T1> (x.a1_); 
}

template <class T1,class T2>
inline
group1<T2> 
group_last( group2<T1,T2> const& x)
{
   return group1<T2> (x.a2_); 
}



template <class T1,class T2,class T3>
inline
group2<T1,T2> 
group_head( group3<T1,T2,T3> const& x)
{
   return group2<T1,T2> (x.a1_,x.a2_); 
}

template <class T1,class T2,class T3>
inline
group1<T3> 
group_last( group3<T1,T2,T3> const& x)
{
   return group1<T3> (x.a3_); 
}



template <class T1,class T2,class T3,class T4>
inline
group3<T1,T2,T3> 
group_head( group4<T1,T2,T3,T4> const& x)
{
   return group3<T1,T2,T3> (x.a1_,x.a2_,x.a3_); 
}

template <class T1,class T2,class T3,class T4>
inline
group1<T4> 
group_last( group4<T1,T2,T3,T4> const& x)
{
   return group1<T4> (x.a4_); 
}



template <class T1,class T2,class T3,class T4,class T5>
inline
group4<T1,T2,T3,T4> 
group_head( group5<T1,T2,T3,T4,T5> const& x)
{
   return group4<T1,T2,T3,T4> (x.a1_,x.a2_,x.a3_,x.a4_); 
}

template <class T1,class T2,class T3,class T4,class T5>
inline
group1<T5> 
group_last( group5<T1,T2,T3,T4,T5> const& x)
{
   return group1<T5> (x.a5_); 
}



template <class T1,class T2,class T3,class T4,class T5,class T6>
inline
group5<T1,T2,T3,T4,T5> 
group_head( group6<T1,T2,T3,T4,T5,T6> const& x)
{
   return group5<T1,T2,T3,T4,T5> (x.a1_,x.a2_,x.a3_,x.a4_,x.a5_); 
}

template <class T1,class T2,class T3,class T4,class T5,class T6>
inline
group1<T6> 
group_last( group6<T1,T2,T3,T4,T5,T6> const& x)
{
   return group1<T6> (x.a6_); 
}



template <class T1,class T2,class T3,class T4,class T5,class T6,class T7>
inline
group6<T1,T2,T3,T4,T5,T6> 
group_head( group7<T1,T2,T3,T4,T5,T6,T7> const& x)
{
   return group6<T1,T2,T3,T4,T5,T6> (x.a1_,x.a2_,x.a3_,x.a4_,x.a5_,x.a6_); 
}

template <class T1,class T2,class T3,class T4,class T5,class T6,class T7>
inline
group1<T7> 
group_last( group7<T1,T2,T3,T4,T5,T6,T7> const& x)
{
   return group1<T7> (x.a7_); 
}



template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8>
inline
group7<T1,T2,T3,T4,T5,T6,T7> 
group_head( group8<T1,T2,T3,T4,T5,T6,T7,T8> const& x)
{
   return group7<T1,T2,T3,T4,T5,T6,T7> (x.a1_,x.a2_,x.a3_,x.a4_,x.a5_,x.a6_,x.a7_); 
}

template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8>
inline
group1<T8> 
group_last( group8<T1,T2,T3,T4,T5,T6,T7,T8> const& x)
{
   return group1<T8> (x.a8_); 
}



template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9>
inline
group8<T1,T2,T3,T4,T5,T6,T7,T8> 
group_head( group9<T1,T2,T3,T4,T5,T6,T7,T8,T9> const& x)
{
   return group8<T1,T2,T3,T4,T5,T6,T7,T8> (x.a1_,x.a2_,x.a3_,x.a4_,x.a5_,x.a6_,x.a7_,x.a8_); 
}

template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9>
inline
group1<T9> 
group_last( group9<T1,T2,T3,T4,T5,T6,T7,T8,T9> const& x)
{
   return group1<T9> (x.a9_); 
}



template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9,class T10>
inline
group9<T1,T2,T3,T4,T5,T6,T7,T8,T9> 
group_head( group10<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10> const& x)
{
   return group9<T1,T2,T3,T4,T5,T6,T7,T8,T9> (x.a1_,x.a2_,x.a3_,x.a4_,x.a5_,x.a6_,x.a7_,x.a8_,x.a9_); 
}

template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9,class T10>
inline
group1<T10> 
group_last( group10<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10> const& x)
{
   return group1<T10> (x.a10_); 
}





} // namespace detail



// helper functions


inline detail::group1< detail::group0 >  
group() { return detail::group1< detail::group0 > ( detail::group0() ); }

template  <class T1, class Var> 
inline
detail::group1< detail::group2<T1, Var const&> >
  group(T1 a1, Var const& var)
{ 
   return detail::group1< detail::group2<T1, Var const&> >
                   ( detail::group2<T1, Var const&> 
                        (a1, var) 
                  );
}

template  <class T1,class T2, class Var> 
inline
detail::group1< detail::group3<T1,T2, Var const&> >
  group(T1 a1,T2 a2, Var const& var)
{ 
   return detail::group1< detail::group3<T1,T2, Var const&> >
                   ( detail::group3<T1,T2, Var const&> 
                        (a1,a2, var) 
                  );
}

template  <class T1,class T2,class T3, class Var> 
inline
detail::group1< detail::group4<T1,T2,T3, Var const&> >
  group(T1 a1,T2 a2,T3 a3, Var const& var)
{ 
   return detail::group1< detail::group4<T1,T2,T3, Var const&> >
                   ( detail::group4<T1,T2,T3, Var const&> 
                        (a1,a2,a3, var) 
                  );
}

template  <class T1,class T2,class T3,class T4, class Var> 
inline
detail::group1< detail::group5<T1,T2,T3,T4, Var const&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4, Var const& var)
{ 
   return detail::group1< detail::group5<T1,T2,T3,T4, Var const&> >
                   ( detail::group5<T1,T2,T3,T4, Var const&> 
                        (a1,a2,a3,a4, var) 
                  );
}

template  <class T1,class T2,class T3,class T4,class T5, class Var> 
inline
detail::group1< detail::group6<T1,T2,T3,T4,T5, Var const&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5, Var const& var)
{ 
   return detail::group1< detail::group6<T1,T2,T3,T4,T5, Var const&> >
                   ( detail::group6<T1,T2,T3,T4,T5, Var const&> 
                        (a1,a2,a3,a4,a5, var) 
                  );
}

template  <class T1,class T2,class T3,class T4,class T5,class T6, class Var> 
inline
detail::group1< detail::group7<T1,T2,T3,T4,T5,T6, Var const&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6, Var const& var)
{ 
   return detail::group1< detail::group7<T1,T2,T3,T4,T5,T6, Var const&> >
                   ( detail::group7<T1,T2,T3,T4,T5,T6, Var const&> 
                        (a1,a2,a3,a4,a5,a6, var) 
                  );
}

template  <class T1,class T2,class T3,class T4,class T5,class T6,class T7, class Var> 
inline
detail::group1< detail::group8<T1,T2,T3,T4,T5,T6,T7, Var const&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7, Var const& var)
{ 
   return detail::group1< detail::group8<T1,T2,T3,T4,T5,T6,T7, Var const&> >
                   ( detail::group8<T1,T2,T3,T4,T5,T6,T7, Var const&> 
                        (a1,a2,a3,a4,a5,a6,a7, var) 
                  );
}

template  <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8, class Var> 
inline
detail::group1< detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var const&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8, Var const& var)
{ 
   return detail::group1< detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var const&> >
                   ( detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var const&> 
                        (a1,a2,a3,a4,a5,a6,a7,a8, var) 
                  );
}

template  <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9, class Var> 
inline
detail::group1< detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var const&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8,T9 a9, Var const& var)
{ 
   return detail::group1< detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var const&> >
                   ( detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var const&> 
                        (a1,a2,a3,a4,a5,a6,a7,a8,a9, var) 
                  );
}


#ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST

template  <class T1, class Var> 
inline
detail::group1< detail::group2<T1, Var&> >
  group(T1 a1, Var& var)
{ 
   return detail::group1< detail::group2<T1, Var&> >
                   ( detail::group2<T1, Var&> 
                        (a1, var) 
                  );
}

template  <class T1,class T2, class Var> 
inline
detail::group1< detail::group3<T1,T2, Var&> >
  group(T1 a1,T2 a2, Var& var)
{ 
   return detail::group1< detail::group3<T1,T2, Var&> >
                   ( detail::group3<T1,T2, Var&> 
                        (a1,a2, var) 
                  );
}

template  <class T1,class T2,class T3, class Var> 
inline
detail::group1< detail::group4<T1,T2,T3, Var&> >
  group(T1 a1,T2 a2,T3 a3, Var& var)
{ 
   return detail::group1< detail::group4<T1,T2,T3, Var&> >
                   ( detail::group4<T1,T2,T3, Var&> 
                        (a1,a2,a3, var) 
                  );
}

template  <class T1,class T2,class T3,class T4, class Var> 
inline
detail::group1< detail::group5<T1,T2,T3,T4, Var&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4, Var& var)
{ 
   return detail::group1< detail::group5<T1,T2,T3,T4, Var&> >
                   ( detail::group5<T1,T2,T3,T4, Var&> 
                        (a1,a2,a3,a4, var) 
                  );
}

template  <class T1,class T2,class T3,class T4,class T5, class Var> 
inline
detail::group1< detail::group6<T1,T2,T3,T4,T5, Var&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5, Var& var)
{ 
   return detail::group1< detail::group6<T1,T2,T3,T4,T5, Var&> >
                   ( detail::group6<T1,T2,T3,T4,T5, Var&> 
                        (a1,a2,a3,a4,a5, var) 
                  );
}

template  <class T1,class T2,class T3,class T4,class T5,class T6, class Var> 
inline
detail::group1< detail::group7<T1,T2,T3,T4,T5,T6, Var&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6, Var& var)
{ 
   return detail::group1< detail::group7<T1,T2,T3,T4,T5,T6, Var&> >
                   ( detail::group7<T1,T2,T3,T4,T5,T6, Var&> 
                        (a1,a2,a3,a4,a5,a6, var) 
                  );
}

template  <class T1,class T2,class T3,class T4,class T5,class T6,class T7, class Var> 
inline
detail::group1< detail::group8<T1,T2,T3,T4,T5,T6,T7, Var&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7, Var& var)
{ 
   return detail::group1< detail::group8<T1,T2,T3,T4,T5,T6,T7, Var&> >
                   ( detail::group8<T1,T2,T3,T4,T5,T6,T7, Var&> 
                        (a1,a2,a3,a4,a5,a6,a7, var) 
                  );
}

template  <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8, class Var> 
inline
detail::group1< detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8, Var& var)
{ 
   return detail::group1< detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var&> >
                   ( detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var&> 
                        (a1,a2,a3,a4,a5,a6,a7,a8, var) 
                  );
}

template  <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9, class Var> 
inline
detail::group1< detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var&> >
  group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8,T9 a9, Var& var)
{ 
   return detail::group1< detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var&> >
                   ( detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var&> 
                        (a1,a2,a3,a4,a5,a6,a7,a8,a9, var) 
                  );
}


#endif  //end- #ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST


} // namespace io

} // namespace boost


#endif   // BOOST_FORMAT_GROUP_HPP

D nix/boost/format/internals.hpp => nix/boost/format/internals.hpp +0 -167
@@ 1,167 0,0 @@
// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream

// ----------------------------------------------------------------------------
// internals.hpp :  internal structs. included by format.hpp
//                              stream_format_state, and format_item
// ----------------------------------------------------------------------------


#ifndef BOOST_FORMAT_INTERNALS_HPP
#define BOOST_FORMAT_INTERNALS_HPP


#include <string>
#include <sstream>

namespace boost {
namespace io {
namespace detail {


// --------------
// set of params that define the format state of a stream

struct stream_format_state 
{
  typedef std::ios   basic_ios;

  std::streamsize width_;
  std::streamsize precision_;
  char fill_; 
  std::ios::fmtflags flags_;

  stream_format_state()       : width_(-1), precision_(-1), fill_(0), flags_(std::ios::dec)  {}
  stream_format_state(basic_ios& os)                  {set_by_stream(os); }

  void apply_on(basic_ios & os) const;                //- applies format_state to the stream
  template<class T> void apply_manip(T manipulator)   //- modifies state by applying manipulator.
       { apply_manip_body<T>( *this, manipulator) ; }
  void reset();                                       //- sets to default state.
  void set_by_stream(const basic_ios& os);            //- sets to os's state.
};  



// --------------
// format_item : stores all parameters that can be defined by directives in the format-string

struct format_item 
{     
  enum pad_values { zeropad = 1, spacepad =2, centered=4, tabulation = 8 };

  enum arg_values { argN_no_posit   = -1, // non-positional directive. argN will be set later.
                    argN_tabulation = -2, // tabulation directive. (no argument read) 
                    argN_ignored    = -3  // ignored directive. (no argument read)
  };
  typedef BOOST_IO_STD ios              basic_ios;
  typedef detail::stream_format_state         stream_format_state;
  typedef std::string           string_t;
  typedef BOOST_IO_STD ostringstream    internal_stream_t;


  int         argN_;           //- argument number (starts at 0,  eg : %1 => argN=0)
                               //  negative values are used for items that don't process
                               //  an argument
  string_t    res_;            //- result of the formatting of this item
  string_t    appendix_;       //- piece of string between this item and the next

  stream_format_state ref_state_;// set by parsing the format_string, is only affected by modify_item
  stream_format_state state_;  // always same as ref_state, _unless_ modified by manipulators 'group(..)'

  // non-stream format-state parameters
  signed int truncate_;        //- is >=0 for directives like %.5s (take 5 chars from the string)
  unsigned int pad_scheme_;    //- several possible padding schemes can mix. see pad_values

  format_item() : argN_(argN_no_posit), truncate_(-1), pad_scheme_(0)  {}

  void compute_states();      // sets states  according to truncate and pad_scheme.
}; 



// -----------------------------------------------------------
// Definitions
// -----------------------------------------------------------

// --- stream_format_state:: -------------------------------------------
inline
void stream_format_state::apply_on(basic_ios & os) const
  // set the state of this stream according to our params
{
      if(width_ != -1)
        os.width(width_);
      if(precision_ != -1)
        os.precision(precision_);
      if(fill_ != 0)
        os.fill(fill_);
      os.flags(flags_);
}

inline
void stream_format_state::set_by_stream(const basic_ios& os) 
  // set our params according to the state of this stream
{
      flags_ = os.flags();
      width_ = os.width();
      precision_ = os.precision();
      fill_ = os.fill();
}

template<class T>  inline
void apply_manip_body( stream_format_state& self,
                       T manipulator) 
  // modify our params according to the manipulator
{
      BOOST_IO_STD stringstream  ss;
      self.apply_on( ss );
      ss << manipulator;
      self.set_by_stream( ss );
}

inline
void stream_format_state::reset() 
  // set our params to standard's default state
{
      width_=-1; precision_=-1; fill_=0; 
      flags_ = std::ios::dec; 
}


// --- format_items:: -------------------------------------------
inline
void format_item::compute_states() 
  // reflect pad_scheme_   on  state_ and ref_state_ 
  //   because some pad_schemes has complex consequences on several state params.
{
  if(pad_scheme_ & zeropad) 
  {
    if(ref_state_.flags_ & std::ios::left) 
    {
      pad_scheme_ = pad_scheme_ & (~zeropad); // ignore zeropad in left alignment
    }
    else 
    { 
      ref_state_.fill_='0'; 
      ref_state_.flags_ |= std::ios::internal;
    }
  }
  state_ = ref_state_;
}


} } } // namespaces boost :: io :: detail


#endif // BOOST_FORMAT_INTERNALS_HPP

D nix/boost/format/internals_fwd.hpp => nix/boost/format/internals_fwd.hpp +0 -65
@@ 1,65 0,0 @@
// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)

// ------------------------------------------------------------------------------
// internals_fwd.hpp :  forward declarations, for internal headers
// ------------------------------------------------------------------------------

#ifndef BOOST_FORMAT_INTERNAL_FWD_HPP
#define BOOST_FORMAT_INTERNAL_FWD_HPP

#include "boost/format/format_fwd.hpp"


namespace boost {
namespace io {

namespace detail {
  struct stream_format_state;
  struct format_item;
}


namespace detail {

  // these functions were intended as methods, 
  // but MSVC have problems with template member functions :

  // defined in format_implementation.hpp :
     template<class T> 
     basic_format&  modify_item_body( basic_format& self, 
                                          int itemN, const T& manipulator);

     template<class T> 
     basic_format&  bind_arg_body( basic_format& self,
                                           int argN, const T& val);

    template<class T> 
    void apply_manip_body( stream_format_state& self,
                           T manipulator);

  // argument feeding (defined in feed_args.hpp ) :
     template<class T> 
     void distribute(basic_format& self, T x);

     template<class T> 
     basic_format& feed(basic_format& self, T x);
 
} // namespace detail

} // namespace io
} // namespace boost


#endif //  BOOST_FORMAT_INTERNAL_FWD_HPP

D nix/boost/format/macros_default.hpp => nix/boost/format/macros_default.hpp +0 -48
@@ 1,48 0,0 @@
// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from R�diger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)

// ------------------------------------------------------------------------------
// macros_default.hpp : configuration for the format library
//                       provides default values for the stl workaround macros
// ------------------------------------------------------------------------------

#ifndef BOOST_FORMAT_MACROS_DEFAULT_HPP
#define BOOST_FORMAT_MACROS_DEFAULT_HPP

// *** This should go to "boost/config/suffix.hpp".

#ifndef BOOST_IO_STD
#  define BOOST_IO_STD std::
#endif

// **** Workaround for io streams, stlport and msvc.
#ifdef BOOST_IO_NEEDS_USING_DECLARATION
namespace boost {
  using std::char_traits;
  using std::basic_ostream;
  using std::basic_ostringstream;
  namespace io {
    using std::basic_ostream;
    namespace detail {
      using std::basic_ios;
      using std::basic_ostream;
      using std::basic_ostringstream;
    }
  }
}
#endif

// ------------------------------------------------------------------------------

#endif // BOOST_FORMAT_MACROS_DEFAULT_HPP

D nix/boost/format/parsing.cc => nix/boost/format/parsing.cc +0 -454
@@ 1,454 0,0 @@
// -*- C++ -*-
//  Boost general library 'format'   ---------------------------
//  See http://www.boost.org for updates, documentation, and revision history.

//  (C) Samuel Krempp 2001
//                  krempp@crans.ens-cachan.fr
//  Permission to copy, use, modify, sell and
//  distribute this software is granted provided this copyright notice appears
//  in all copies. This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.

// ideas taken from Rudiger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)

// ------------------------------------------------------------------------------
// parsing.hpp :  implementation of the parsing member functions
//                      ( parse, parse_printf_directive)
// ------------------------------------------------------------------------------


#ifndef BOOST_FORMAT_PARSING_HPP
#define BOOST_FORMAT_PARSING_HPP


#include <boost/format.hpp>
#include <boost/throw_exception.hpp>
#include <boost/assert.hpp>


namespace boost {
namespace io {
namespace detail {

  template<class Stream> inline
  bool wrap_isdigit(char c, Stream &os) 
  {
#ifndef BOOST_NO_LOCALE_ISIDIGIT
    return std::isdigit(c, os.rdbuf()->getloc() );
# else
    using namespace std;
    return isdigit(c); 
#endif 
  } //end- wrap_isdigit(..)

  template<class Res> inline
  Res str2int(const std::string& s, 
              std::string::size_type start, 
              BOOST_IO_STD ios &os,
              const Res = Res(0)  ) 
    // Input : char string, with starting index
    //         a basic_ios& merely to call its widen/narrow member function in the desired locale.
    // Effects : reads s[start:] and converts digits into an integral n, of type Res
    // Returns : n
  {
    Res n = 0;
    while(start<s.size() && wrap_isdigit(s[start], os) ) {
      char cur_ch = s[start];
      BOOST_ASSERT(cur_ch != 0 ); // since we called isdigit, this should not happen.
      n *= 10;
      n += cur_ch - '0'; // 22.2.1.1.2 of the C++ standard
      ++start;
    }
    return n;
  }

  void skip_asterisk(const std::string & buf, 
                     std::string::size_type * pos_p,
                     BOOST_IO_STD ios &os)
    // skip printf's "asterisk-fields" directives in the format-string buf
    // Input : char string, with starting index *pos_p
    //         a basic_ios& merely to call its widen/narrow member function in the desired locale.
    // Effects : advance *pos_p by skipping printf's asterisk fields.
    // Returns : nothing
  {
    using namespace std;
    BOOST_ASSERT( pos_p != 0);
    if(*pos_p >= buf.size() ) return;
    if(buf[ *pos_p]=='*') {
      ++ (*pos_p);
      while (*pos_p < buf.size() && wrap_isdigit(buf[*pos_p],os)) ++(*pos_p);
      if(buf[*pos_p]=='$') ++(*pos_p);
    }
  }


  inline void maybe_throw_exception( unsigned char exceptions)
    // auxiliary func called by parse_printf_directive
    // for centralising error handling
    // it either throws if user sets the corresponding flag, or does nothing.
  {
    if(exceptions & io::bad_format_string_bit)
          boost::throw_exception(io::bad_format_string());
  }
    


  bool parse_printf_directive(const std::string & buf,
                              std::string::size_type * pos_p,
                              detail::format_item * fpar,
                              BOOST_IO_STD ios &os,
                              unsigned char exceptions)
    // Input   : a 'printf-directive' in the format-string, starting at buf[ *pos_p ]
    //           a basic_ios& merely to call its widen/narrow member function in the desired locale.
    //           a bitset'excpetions' telling whether to throw exceptions on errors.
    // Returns : true if parse somehow succeeded (possibly ignoring errors if exceptions disabled) 
    //           false if it failed so bad that the directive should be printed verbatim
    // Effects : - *pos_p is incremented so that buf[*pos_p] is the first char after the directive
    //           - *fpar is set with the parameters read in the directive
  {
    typedef format_item  format_item_t;
    BOOST_ASSERT( pos_p != 0);
    std::string::size_type       &i1 = *pos_p,      
                                                        i0; 
    fpar->argN_ = format_item_t::argN_no_posit;  // if no positional-directive

    bool in_brackets=false;
    if(buf[i1]=='|')
      {
        in_brackets=true;
        if( ++i1 >= buf.size() ) {
          maybe_throw_exception(exceptions);
          return false;
        }
      }

    // the flag '0' would be picked as a digit for argument order, but here it's a flag :
    if(buf[i1]=='0') 
      goto parse_flags;

    // handle argument order (%2$d)  or possibly width specification: %2d
    i0 = i1;  // save position before digits
    while (i1 < buf.size() && wrap_isdigit(buf[i1], os))
      ++i1;
    if (i1!=i0) 
      {
        if( i1 >= buf.size() ) {
          maybe_throw_exception(exceptions);
          return false;
        }
        int n=str2int(buf,i0, os, int(0) );
        
        // %N% case : this is already the end of the directive
        if( buf[i1] == '%' ) 
          {
            fpar->argN_ = n-1;
            ++i1;
            if( in_brackets) 
              maybe_throw_exception(exceptions); 
              // but don't return.  maybe "%" was used in lieu of '$', so we go on.
            else return true;
          }

        if ( buf[i1]=='$' ) 
          {
            fpar->argN_ = n-1;
            ++i1;
          } 
        else  
          {
            // non-positionnal directive
            fpar->ref_state_.width_ = n;
            fpar->argN_  = format_item_t::argN_no_posit;
            goto parse_precision;
          }
      }
    
  parse_flags: 
    // handle flags
    while ( i1 <buf.size()) // as long as char is one of + - = # 0 l h   or ' '
      {  
        // misc switches
        switch (buf[i1]) 
          {
          case '\'' : break; // no effect yet. (painful to implement)
          case 'l':
          case 'h':  // short/long modifier : for printf-comaptibility (no action needed)
             break;
          case '-':
            fpar->ref_state_.flags_ |= std::ios::left;
            break;
          case '=':
            fpar->pad_scheme_ |= format_item_t::centered;
            break;
          case ' ':
            fpar->pad_scheme_ |= format_item_t::spacepad;
            break;
          case '+':
            fpar->ref_state_.flags_ |= std::ios::showpos;
            break;
          case '0':
            fpar->pad_scheme_ |= format_item_t::zeropad; 
            // need to know alignment before really setting flags,
            // so just add 'zeropad' flag for now, it will be processed later.
            break;
          case '#':
            fpar->ref_state_.flags_ |= std::ios::showpoint | std::ios::showbase;
            break;
          default:
            goto parse_width;
          }
        ++i1;
      } // loop on flag.
    if( i1>=buf.size()) {
      maybe_throw_exception(exceptions);
      return true; 
    }

  parse_width:
    // handle width spec
    skip_asterisk(buf, &i1, os); // skips 'asterisk fields' :  *, or *N$
    i0 = i1;  // save position before digits
    while (i1<buf.size() && wrap_isdigit(buf[i1], os))
      i1++;
    
    if (i1!=i0) 
      { fpar->ref_state_.width_ = str2int( buf,i0, os, std::streamsize(0) ); }

  parse_precision:
    if( i1>=buf.size()) { 
      maybe_throw_exception(exceptions);
      return true;
    }
    // handle precision spec
    if (buf[i1]=='.')  
      {
        ++i1;
        skip_asterisk(buf, &i1, os);
        i0 = i1;  // save position before digits
        while (i1<buf.size() && wrap_isdigit(buf[i1], os))
          ++i1;

        if(i1==i0)
          fpar->ref_state_.precision_ = 0;
        else 
          fpar->ref_state_.precision_ = str2int(buf,i0, os, std::streamsize(0) );
      }
    
    // handle  formatting-type flags :
    while( i1<buf.size() && 
           ( buf[i1]=='l' || buf[i1]=='L' || buf[i1]=='h') )
      ++i1;
    if( i1>=buf.size()) {
      maybe_throw_exception(exceptions);
      return true;
    }
    
    if( in_brackets && buf[i1]=='|' ) 
      {
        ++i1;
        return true;
      }
    switch (buf[i1])  
      {
      case 'X':
        fpar->ref_state_.flags_ |= std::ios::uppercase;
      case 'p': // pointer => set hex.
      case 'x':
        fpar->ref_state_.flags_ &= ~std::ios::basefield;
        fpar->ref_state_.flags_ |= std::ios::hex;
        break;
      
      case 'o':
        fpar->ref_state_.flags_ &= ~std::ios::basefield;
        fpar->ref_state_.flags_ |=  std::ios::oct;
        break;

      case 'E':
        fpar->ref_state_.flags_ |=  std::ios::uppercase;
      case 'e':
        fpar->ref_state_.flags_ &= ~std::ios::floatfield;
        fpar->ref_state_.flags_ |=  std::ios::scientific;

        fpar->ref_state_.flags_ &= ~std::ios::basefield;
        fpar->ref_state_.flags_ |=  std::ios::dec;
        break;
      
      case 'f':
        fpar->ref_state_.flags_ &= ~std::ios::floatfield;
        fpar->ref_state_.flags_ |=  std::ios::fixed;
      case 'u':
      case 'd':
      case 'i':
        fpar->ref_state_.flags_ &= ~std::ios::basefield;
        fpar->ref_state_.flags_ |=  std::ios::dec;
        break;

      case 'T':
        ++i1;
        if( i1 >= buf.size())
          maybe_throw_exception(exceptions);
        else
          fpar->ref_state_.fill_ = buf[i1];
        fpar->pad_scheme_ |= format_item_t::tabulation;
        fpar->argN_ = format_item_t::argN_tabulation; 
        break;
      case 't': 
        fpar->ref_state_.fill_ = ' ';
        fpar->pad_scheme_ |= format_item_t::tabulation;
        fpar->argN_ = format_item_t::argN_tabulation; 
        break;

      case 'G':
        fpar->ref_state_.flags_ |= std::ios::uppercase;
        break;
      case 'g': // 'g' conversion is default for floats.
        fpar->ref_state_.flags_ &= ~std::ios::basefield;
        fpar->ref_state_.flags_ |=  std::ios::dec;

        // CLEAR all floatield flags, so stream will CHOOSE
        fpar->ref_state_.flags_ &= ~std::ios::floatfield; 
        break;

      case 'C':
      case 'c': 
        fpar->truncate_ = 1;
        break;
      case 'S':
      case 's': 
        fpar->truncate_ = fpar->ref_state_.precision_;
        fpar->ref_state_.precision_ = -1;
        break;
      case 'n' :  
        fpar->argN_ = format_item_t::argN_ignored;
        break;
      default: 
        maybe_throw_exception(exceptions);
      }
    ++i1;

    if( in_brackets )
      {
        if( i1<buf.size() && buf[i1]=='|' ) 
          {
            ++i1;
            return true;
          }
        else  maybe_throw_exception(exceptions);
      }
    return true;
  }

} // detail namespace
} // io namespace


// -----------------------------------------------
//  format :: parse(..)

void basic_format::parse(const string_t & buf) 
  // parse the format-string
{
    using namespace std;
    const char arg_mark = '%';
    bool ordered_args=true; 
    int max_argN=-1;
    string_t::size_type i1=0;
    int num_items=0;
    
    // A: find upper_bound on num_items and allocates arrays
    i1=0; 
    while( (i1=buf.find(arg_mark,i1)) != string::npos ) 
    {
      if( i1+1 >= buf.size() ) {
        if(exceptions() & io::bad_format_string_bit)
          boost::throw_exception(io::bad_format_string()); // must not end in "bla bla %"
        else break; // stop there, ignore last '%'
      }
      if(buf[i1+1] == buf[i1] ) { i1+=2; continue; } // escaped "%%" / "##"
      ++i1;
      
      // in case of %N% directives, dont count it double (wastes allocations..) :
      while(i1 < buf.size() && io::detail::wrap_isdigit(buf[i1],oss_)) ++i1;
      if( i1 < buf.size() && buf[i1] == arg_mark ) ++ i1;

      ++num_items;
    }
    items_.assign( num_items, format_item_t() );
    
    // B: Now the real parsing of the format string :
    num_items=0;
    i1 = 0;
    string_t::size_type i0 = i1;
    bool special_things=false;
    int cur_it=0;
    while( (i1=buf.find(arg_mark,i1)) != string::npos ) 
    {
      string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;

      if( buf[i1+1] == buf[i1] ) // escaped mark, '%%'
      {
        piece += buf.substr(i0, i1-i0) + buf[i1]; 
        i1+=2; i0=i1;
        continue; 
      }
      BOOST_ASSERT(  static_cast<unsigned int>(cur_it) < items_.size() || cur_it==0);

      if(i1!=i0) piece += buf.substr(i0, i1-i0);
      ++i1;
      
      bool parse_ok;
      parse_ok = io::detail::parse_printf_directive(buf, &i1, &items_[cur_it], oss_, exceptions());
      if( ! parse_ok ) continue; // the directive will be printed verbatim

      i0=i1;
      items_[cur_it].compute_states(); // process complex options, like zeropad, into stream params.

      int argN=items_[cur_it].argN_;
      if(argN == format_item_t::argN_ignored)
        continue;
      if(argN ==format_item_t::argN_no_posit)
        ordered_args=false;
      else if(argN == format_item_t::argN_tabulation) special_things=true;
      else if(argN > max_argN) max_argN = argN;
      ++num_items;
      ++cur_it;
    } // loop on %'s
    BOOST_ASSERT(cur_it == num_items);
    
    // store the final piece of string
    string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
    piece += buf.substr(i0);
    
    if( !ordered_args) 
    {
      if(max_argN >= 0 )  // dont mix positional with non-positionnal directives
        {
          if(exceptions() & io::bad_format_string_bit)
            boost::throw_exception(io::bad_format_string());
          // else do nothing. => positionnal arguments are processed as non-positionnal
        }
      // set things like it would have been with positional directives :
      int non_ordered_items = 0;
      for(int i=0; i< num_items; ++i)
        if(items_[i].argN_ == format_item_t::argN_no_posit) 
          {
            items_[i].argN_ = non_ordered_items;
            ++non_ordered_items;
          }
      max_argN = non_ordered_items-1;
    }
    
    // C: set some member data :
    items_.resize(num_items);

    if(special_things) style_ |= special_needs;
    num_args_ = max_argN + 1;
    if(ordered_args) style_ |=  ordered;
    else style_ &= ~ordered;
}

} // namespace boost


#endif //  BOOST_FORMAT_PARSING_HPP

D nix/boost/throw_exception.hpp => nix/boost/throw_exception.hpp +0 -47
@@ 1,47 0,0 @@
#ifndef BOOST_THROW_EXCEPTION_HPP_INCLUDED
#define BOOST_THROW_EXCEPTION_HPP_INCLUDED

// MS compatible compilers support #pragma once

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

//
//  boost/throw_exception.hpp
//
//  Copyright (c) 2002 Peter Dimov and Multi Media Ltd.
//
//  Permission to copy, use, modify, sell and distribute this software
//  is granted provided this copyright notice appears in all copies.
//  This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.
//
//  http://www.boost.org/libs/utility/throw_exception.html
//

//#include <boost/config.hpp>

#ifdef BOOST_NO_EXCEPTIONS
# include <exception>
#endif

namespace boost
{

#ifdef BOOST_NO_EXCEPTIONS

void throw_exception(std::exception const & e); // user defined

#else

template<class E> void throw_exception(E const & e)
{
    throw e;
}

#endif

} // namespace boost

#endif // #ifndef BOOST_THROW_EXCEPTION_HPP_INCLUDED

M nix/libstore/build.cc => nix/libstore/build.cc +214 -219
@@ 7,14 7,14 @@
#include "local-store.hh"
#include "util.hh"
#include "archive.hh"
#include "affinity.hh"
#include "builtins.hh"
#include "spawn.hh"

#include <map>
#include <sstream>
#include <algorithm>
#include <regex>
#include <format>
#include <string_view>

#include <limits.h>
#include <time.h>


@@ 29,7 29,7 @@
#include <errno.h>
#include <stdio.h>
#include <cstring>
#include <stdint.h>
#include <cassert>

#include <pwd.h>
#include <grp.h>


@@ 195,7 195,7 @@ public:
        abort();
    }

    void trace(const format & f);
    void trace(std::string_view s);

    string getName()
    {


@@ 372,8 372,7 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
    assert(waitees.find(waitee) != waitees.end());
    waitees.erase(waitee);

    trace(format("waitee `%1%' done; %2% left") %
        waitee->name % waitees.size());
    trace(std::format("waitee `{}' done; {} left", waitee->name, waitees.size()));

    if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed;



@@ 414,9 413,9 @@ void Goal::amDone(ExitCode result)
}


void Goal::trace(const format & f)
void Goal::trace(std::string_view f)
{
    debug(format("%1%: %2%") % name % f);
    debug(std::format("{}: {}", name, f));
}




@@ 483,34 482,34 @@ void UserLock::acquire()
    /* Get the members of the build-users-group. */
    struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
    if (!gr)
        throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
            % settings.buildUsersGroup);
        throw Error(std::format("the group `{}' specified in `build-users-group' does not exist",
            settings.buildUsersGroup));
    gid = gr->gr_gid;

    /* Copy the result of getgrnam. */
    Strings users;
    for (char * * p = gr->gr_mem; *p; ++p) {
        debug(format("found build user `%1%'") % *p);
        debug(std::format("found build user `{}'", *p));
        users.push_back(*p);
    }

    if (users.empty())
        throw Error(format("the build users group `%1%' has no members")
            % settings.buildUsersGroup);
        throw Error(std::format("the build users group `{}' has no members",
            settings.buildUsersGroup));

    /* Find a user account that isn't currently in use for another
       build. */
    for (auto& i : users) {
        debug(format("trying user `%1%'") % i);
        debug(std::format("trying user `{}'", i));

        struct passwd * pw = getpwnam(i.c_str());
        if (!pw)
            throw Error(format("the user `%1%' in the group `%2%' does not exist")
                % i % settings.buildUsersGroup);
            throw Error(std::format("the user `{}' in the group `{}' does not exist",
                i, settings.buildUsersGroup));

        createDirs(settings.nixStateDir + "/userpool");

        fnUserLock = (format("%1%/userpool/%2%") % settings.nixStateDir % pw->pw_uid).str();
        fnUserLock = std::format("{}/userpool/{}", settings.nixStateDir, pw->pw_uid);

        if (lockedPaths.find(fnUserLock) != lockedPaths.end())
            /* We already have a lock on this one. */


@@ 518,7 517,7 @@ void UserLock::acquire()

        AutoCloseFD fd = open(fnUserLock.c_str(), O_RDWR | O_CREAT, 0600);
        if (fd == -1)
            throw SysError(format("opening user lock `%1%'") % fnUserLock);
            throw SysError(std::format("opening user lock `{}'", fnUserLock));
        closeOnExec(fd);

        if (lockFile(fd, ltWrite, false)) {


@@ 529,8 528,8 @@ void UserLock::acquire()

            /* Sanity check... */
            if (uid == getuid() || uid == geteuid())
                throw Error(format("the build user should not be a member of `%1%'")
                    % settings.buildUsersGroup);
                throw Error(std::format("the build user should not be a member of `{}'",
                    settings.buildUsersGroup));

            /* Get the list of supplementary groups of this build user.  This
               is usually either empty or contains a group such as "kvm".  */


@@ 539,7 538,7 @@ void UserLock::acquire()
            int err = getgrouplist(pw->pw_name, pw->pw_gid,
                supplementaryGIDs.data(), &ngroups);
            if (err == -1)
                throw Error(format("failed to get list of supplementary groups for ‘%1%’") % pw->pw_name);
                throw Error(std::format("failed to get list of supplementary groups for `{}'", pw->pw_name));

            supplementaryGIDs.resize(ngroups);



@@ 547,9 546,9 @@ void UserLock::acquire()
        }
    }

    throw Error(format("all build users are currently in use; "
        "consider creating additional users and adding them to the `%1%' group")
        % settings.buildUsersGroup);
    throw Error(std::format("all build users are currently in use; "
        "consider creating additional users and adding them to the `{}' group",
        settings.buildUsersGroup));
}




@@ 582,7 581,7 @@ string rewriteHashes(string s, const HashRewrites & rewrites)
        assert(i.first.size() == i.second.size());
        size_t j = 0;
        while ((j = s.find(i.first, j)) != string::npos) {
            debug(format("rewriting @ %1%") % j);
            debug(std::format("rewriting @ {}", j));
            s.replace(j, i.second.size(), i.second);
        }
    }


@@ 716,7 715,7 @@ public:

    void timedOut() override;

    string key()
    string key() override
    {
        /* Ensure that derivations get built in order of their name,
           i.e. a derivation named "aardvark" always comes before


@@ 725,7 724,7 @@ public:
        return "b$" + storePathToName(drvPath) + "$" + drvPath;
    }

    void work();
    void work() override;

    Path getDrvPath()
    {


@@ 771,8 770,8 @@ private:
    void deleteTmpDir(bool force);

    /* Callback used by the worker to write to the log. */
    void handleChildOutput(int fd, const string & data);
    void handleEOF(int fd);
    void handleChildOutput(int fd, const string & data) override;
    void handleEOF(int fd) override;

    /* Return the set of (in)valid paths. */
    PathSet checkPathValidity(bool returnValid, bool checkHash);


@@ 806,7 805,7 @@ DerivationGoal::DerivationGoal(const Path & drvPath, const StringSet & wantedOut
{
    this->drvPath = drvPath;
    state = &DerivationGoal::init;
    name = (format("building of `%1%'") % drvPath).str();
    name = std::format("building of `{}'", drvPath);
    trace("created");

    /* Prevent the .chroot directory from being


@@ 862,7 861,7 @@ void DerivationGoal::killChild()
void DerivationGoal::timedOut()
{
    if (settings.printBuildTrace)
        printMsg(lvlError, format("@ build-failed %1% - timeout") % drvPath);
        printMsg(lvlError, std::format("@ build-failed {} - timeout", drvPath));
    killChild();
    done(BuildResult::TimedOut);
}


@@ 896,7 895,7 @@ void DerivationGoal::init()
    trace("init");

    if (settings.readOnlyMode)
        throw Error(format("cannot build derivation `%1%' - no write access to the store") % drvPath);
        throw Error(std::format("cannot build derivation `{}' - no write access to the store", drvPath));

    /* The first thing to do is to make sure that the derivation
       exists.  If it doesn't, it may be created through a


@@ 917,7 916,7 @@ void DerivationGoal::haveDerivation()
    trace("loading derivation");

    if (nrFailed != 0) {
        printMsg(lvlError, format("cannot build missing derivation ‘%1%’") % drvPath);
        printMsg(lvlError, std::format("cannot build missing derivation `{}'", drvPath));
        done(BuildResult::MiscFailure);
        return;
    }


@@ 968,7 967,7 @@ void DerivationGoal::outputsSubstituted()
    trace("all outputs substituted (maybe)");

    if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback)
        throw Error(format("some substitutes for the outputs of derivation `%1%' failed (usually happens due to networking issues); try `--fallback' to build derivation from source ") % drvPath);
        throw Error(std::format("some substitutes for the outputs of derivation `{}' failed (usually happens due to networking issues); try `--fallback' to build derivation from source ", drvPath));

    /*  If the substitutes form an incomplete closure, then we should
        build the dependencies of this derivation, but after that, we


@@ 993,7 992,7 @@ void DerivationGoal::outputsSubstituted()
        return;
    }
    if (buildMode == bmCheck && nrInvalid > 0)
        throw Error(format("`%1%' is missing outputs; build it normally before using `--check'") % drvPath);
        throw Error(std::format("`{}' is missing outputs; build it normally before using `--check'", drvPath));

    /* Otherwise, at least one of the output paths could not be
       produced using a substitute.  So we have to build instead. */


@@ 1051,7 1050,7 @@ void DerivationGoal::repairClosure()
    PathSet broken;
    for (auto& i : outputClosure) {
        if (worker.store.pathContentsGood(i)) continue;
        printMsg(lvlError, format("found corrupted or missing path `%1%' in the output closure of `%2%'") % i % drvPath);
        printMsg(lvlError, std::format("found corrupted or missing path `{}' in the output closure of `{}'", i, drvPath));
        Path drvPath2 = outputsToDrv[i];
        if (drvPath2 == "")
            addWaitee(worker.makeSubstitutionGoal(i, true));


@@ 1072,7 1071,7 @@ void DerivationGoal::closureRepaired()
{
    trace("closure repaired");
    if (nrFailed > 0)
        throw Error(format("some paths in the output closure of derivation ‘%1%’ could not be repaired") % drvPath);
        throw Error(std::format("some paths in the output closure of derivation `{}' could not be repaired", drvPath));
    done(BuildResult::AlreadyValid);
}



@@ 1083,8 1082,8 @@ void DerivationGoal::inputsRealised()

    if (nrFailed != 0) {
        printMsg(lvlError,
            format("cannot build derivation `%1%': %2% dependencies couldn't be built")
            % drvPath % nrFailed);
            std::format("cannot build derivation `{}': {} dependencies couldn't be built",
            drvPath, nrFailed));
        done(BuildResult::DependencyFailed);
        return;
    }


@@ 1099,7 1098,7 @@ void DerivationGoal::inputsRealised()

    /* The outputs are referenceable paths. */
    for (auto& i : drv.outputs) {
        debug(format("building path `%1%'") % i.second.path);
        debug(std::format("building path `{}'", i.second.path));
        allPaths.insert(i.second.path);
    }



@@ 1117,15 1116,15 @@ void DerivationGoal::inputsRealised()
                computeFSClosure(worker.store, inDrv.outputs[j].path, inputPaths);
            else
                throw Error(
                    format("derivation `%1%' requires non-existent output `%2%' from input derivation `%3%'")
                    % drvPath % j % i.first);
                    std::format("derivation `{}' requires non-existent output `{}' from input derivation `{}'",
                    drvPath, j, i.first));
    }

    /* Second, the input sources. */
    for (auto& i : drv.inputSrcs)
        computeFSClosure(worker.store, i, inputPaths);

    debug(format("added input paths %1%") % showPaths(inputPaths));
    debug(std::format("added input paths {}", showPaths(inputPaths)));

    allPaths.insert(inputPaths.begin(), inputPaths.end());



@@ 1187,8 1186,8 @@ void DerivationGoal::tryToBuild()
       goal to sleep until another goal finishes, then try again. */
    for (auto& i : drv.outputs)
        if (pathIsLockedByMe(i.second.path)) {
            debug(format("putting derivation `%1%' to sleep because `%2%' is locked by another goal")
                % drvPath % i.second.path);
            debug(std::format("putting derivation `{}' to sleep because `{}' is locked by another goal",
                drvPath, i.second.path));
            worker.waitForAnyGoal(shared_from_this());
            return;
        }


@@ 1212,7 1211,7 @@ void DerivationGoal::tryToBuild()
       build this derivation, so no further checks are necessary. */
    validPaths = checkPathValidity(true, buildMode == bmRepair);
    if (buildMode != bmCheck && validPaths.size() == drv.outputs.size()) {
        debug(format("skipping build of derivation `%1%', someone beat us to it") % drvPath);
        debug(std::format("skipping build of derivation `{}', someone beat us to it", drvPath));
        outputLocks.setDeletion(true);
        outputLocks.unlock();
        done(BuildResult::AlreadyValid);


@@ 1229,7 1228,7 @@ void DerivationGoal::tryToBuild()
        Path path = i.second.path;
        if (worker.store.isValidPath(path)) continue;
        if (!pathExists(path)) continue;
        debug(format("removing invalid path `%1%'") % path);
        debug(std::format("removing invalid path `{}'", path));
        deletePath(path);
    }



@@ 1284,8 1283,8 @@ void DerivationGoal::tryToBuild()
        outputLocks.unlock();
        buildUser.release();
        if (settings.printBuildTrace)
            printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
                % drvPath % 0 % e.msg());
            printMsg(lvlError, std::format("@ build-failed {} - {} {}",
                drvPath, 0, e.msg()));
        worker.permanentFailure = true;
        done(BuildResult::InputRejected, e.msg());
        return;


@@ 1303,11 1302,11 @@ void replaceValidPath(const Path & storePath, const Path tmpPath)
       tmpPath (the replacement), so we have to move it out of the
       way first.  We'd better not be interrupted here, because if
       we're repairing (say) Glibc, we end up with a broken system. */
    Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % rand()).str();
    Path oldPath = std::format("{}.old-{}-{}", storePath, getpid(), rand());
    if (pathExists(storePath))
        rename(storePath.c_str(), oldPath.c_str());
    if (rename(tmpPath.c_str(), storePath.c_str()) == -1)
        throw SysError(format("moving `%1%' to `%2%'") % tmpPath % storePath);
        throw SysError(std::format("moving `{}' to `{}'", tmpPath, storePath));
    if (pathExists(oldPath))
        deletePath(oldPath);
}


@@ 1349,7 1348,7 @@ static void secureFilePerms(Path path, bool allowSpecialFiles = false)
    /* FALLTHROUGH */

  default:
    throw Error(format("file `%1%' has an unsupported type") % path);
    throw Error(std::format("file `{}' has an unsupported type", path));
  }
}



@@ 1373,7 1372,7 @@ void DerivationGoal::buildDone()
        status = pid.wait(true);
    }

    debug(format("builder process for `%1%' finished") % drvPath);
    debug(std::format("builder process for `{}' finished", drvPath));

    /* So the child is gone now. */
    worker.childTerminated(savedPid);


@@ 1436,8 1435,8 @@ void DerivationGoal::buildDone()
            if (diskFull)
                printMsg(lvlError, "note: build failure may have been caused by lack of free disk space");

            throw BuildError(format("builder for `%1%' %2%")
                % drvPath % statusToString(status));
            throw BuildError(std::format("builder for `{}' {}",
                drvPath, statusToString(status)));
        }

	if (fixedOutput) {


@@ 1451,8 1450,8 @@ void DerivationGoal::buildDone()
		    copyFileRecursively(output, pivot, true);
		    int err = rename(pivot.c_str(), output.c_str());
		    if (err != 0)
			throw SysError(format("renaming `%1%' to `%2%'")
				       % pivot % output);
			throw SysError(std::format("renaming `{}' to `{}'",
				       pivot, output));
		}
	    }
	}


@@ 1496,20 1495,20 @@ void DerivationGoal::buildDone()

        if (hook && WIFEXITED(status) && WEXITSTATUS(status) == 101) {
            if (settings.printBuildTrace)
                printMsg(lvlError, format("@ build-failed %1% - timeout") % drvPath);
                printMsg(lvlError, std::format("@ build-failed {} - timeout", drvPath));
            st = BuildResult::TimedOut;
        }

        else if (hook && (!WIFEXITED(status) || WEXITSTATUS(status) != 100)) {
            if (settings.printBuildTrace)
                printMsg(lvlError, format("@ hook-failed %1% - %2% %3%")
                    % drvPath % status % e.msg());
                printMsg(lvlError, std::format("@ hook-failed {} - {} {}",
                    drvPath, status, e.msg()));
        }

        else {
            if (settings.printBuildTrace)
                printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
                    % drvPath % 1 % e.msg());
                printMsg(lvlError, std::format("@ build-failed {} - {} {}",
                    drvPath, 1, e.msg()));

            st =
                statusOk(status) ? BuildResult::OutputRejected :


@@ 1536,7 1535,7 @@ void DerivationGoal::buildDone()
    buildUser.release();

    if (settings.printBuildTrace)
        printMsg(lvlError, format("@ build-succeeded %1% -") % drvPath);
        printMsg(lvlError, std::format("@ build-succeeded {} -", drvPath));

    done(BuildResult::Built);
}


@@ 1549,10 1548,10 @@ HookReply DerivationGoal::tryBuildHook()
    if (!worker.hook) {
	Strings args = {
	    "offload",
	    settings.thisSystem.c_str(),
            (format("%1%") % settings.maxSilentTime).str().c_str(),
            (format("%1%") % settings.printBuildTrace).str().c_str(),
            (format("%1%") % settings.buildTimeout).str().c_str()
	    settings.thisSystem,
        std::format("{}", settings.maxSilentTime),
        std::format("{}", settings.printBuildTrace),
        std::format("{}", settings.buildTimeout)
	};

        worker.hook = std::make_shared<Agent>(settings.guixProgram, args);


@@ 1565,9 1564,9 @@ HookReply DerivationGoal::tryBuildHook()
    for (auto& i : features) checkStoreName(i); /* !!! abuse */

    /* Send the request to the hook. */
    writeLine(worker.hook->toAgent.writeSide, (format("%1% %2% %3% %4%")
        % (worker.getNrLocalBuilds() < settings.maxBuildJobs ? "1" : "0")
        % drv.platform % drvPath % concatStringsSep(",", features)).str());
    writeLine(worker.hook->toAgent.writeSide, std::format("{} {} {} {}",
        (worker.getNrLocalBuilds() < settings.maxBuildJobs ? "1" : "0"),
        drv.platform, drvPath, concatStringsSep(",", features)));

    /* Read the first line of input, which should be a word indicating
       whether the hook wishes to perform the build. */


@@ 1582,14 1581,14 @@ HookReply DerivationGoal::tryBuildHook()
        writeToStderr(s);
    }

    debug(format("hook reply is `%1%'") % reply);
    debug(std::format("hook reply is `{}'", reply));

    if (reply == "decline" || reply == "postpone")
        return reply == "decline" ? rpDecline : rpPostpone;
    else if (reply != "accept")
        throw Error(format("bad hook reply `%1%'") % reply);
        throw Error(std::format("bad hook reply `{}'", reply));

    printMsg(lvlTalkative, format("using hook to build path(s) %1%") % showPaths(missingPaths));
    printMsg(lvlTalkative, std::format("using hook to build path(s) {}", showPaths(missingPaths)));

    hook = worker.hook;
    worker.hook.reset();


@@ 1624,8 1623,8 @@ HookReply DerivationGoal::tryBuildHook()
    worker.childStarted(shared_from_this(), hook->pid, fds, false, true);

    if (settings.printBuildTrace)
        printMsg(lvlError, format("@ build-started %1% - %2% %3% %4%")
            % drvPath % drv.platform % logFile % hook->pid);
        printMsg(lvlError, std::format("@ build-started {} - {} {} {}",
            drvPath, drv.platform, logFile, pid_t(hook->pid)));

    return rpAccept;
}


@@ 1634,7 1633,7 @@ HookReply DerivationGoal::tryBuildHook()
void chmod_(const Path & path, mode_t mode)
{
    if (chmod(path.c_str(), mode) == -1)
        throw SysError(format("setting permissions on `%1%'") % path);
        throw SysError(std::format("setting permissions on `{}'", path));
}




@@ 1652,7 1651,7 @@ static void initializeUserNamespace(pid_t child,
				    bool haveCapSetGID = false)
{
    writeFile("/proc/" + std::to_string(child) + "/uid_map",
	      (format("%d %d 1") % guestUID % hostUID).str());
	    std::format("{} {} 1", guestUID, hostUID));

    if (!haveCapSetGID && !extraGIDs.empty()) {
	try {


@@ 1668,8 1667,8 @@ static void initializeUserNamespace(pid_t child,

	    runProgram("newgidmap", true, args);
	    printMsg(lvlChatty,
		     format("mapped %1% extra GIDs into namespace of PID %2%")
		     % extraGIDs.size() % child);
		     std::format("mapped {} extra GIDs into namespace of PID {}",
		     extraGIDs.size(), child));

	    return;
	} catch (const ExecError &e) {


@@ 1680,10 1679,10 @@ static void initializeUserNamespace(pid_t child,
    if (!haveCapSetGID)
	writeFile("/proc/" + std::to_string(child) + "/setgroups", "deny");

    auto content = (format("%d %d 1\n") % guestGID % hostGID).str();
    auto content = std::format("{} {} 1\n", guestGID, hostGID);
    if (haveCapSetGID) {
	for (auto &mapping: extraGIDs) {
	    content += (format("%d %d 1\n") % mapping.second % mapping.first).str();
	    content += std::format("{} {} 1\n", mapping.second, mapping.first);
	}
    }
    writeFile("/proc/" + std::to_string(child) + "/gid_map", content);


@@ 1963,7 1962,7 @@ static void prepareSlirpChrootAction(SpawnContext & sctx)
            struct stat st;
            if(stat(fs.c_str(), &st) != 0) {
                if(errno == EACCES) continue; /* Not accessible anyway */
                else throw SysError(format("stat of `%1%'") % fs);
                else throw SysError(std::format("stat of `{}'", fs));
            }

            ctx.readOnlyFilesInChroot.insert(fs);


@@ 2408,7 2407,7 @@ void DerivationGoal::execBuilderOrBuiltin(SpawnContext & ctx)
                buildDrv(drv, drvPath, output);
            }
            else
                throw Error(format("unsupported builtin function '%1%'") % string(drv.builder, 8));
                throw Error(std::format("unsupported builtin function '{}'", string(drv.builder, 8)));
            _exit(0);
        } catch (std::exception & e) {
            writeFull(STDERR_FILENO, "error: " + string(e.what()) + "\n");


@@ 2427,7 2426,7 @@ void DerivationGoal::execBuilderOrBuiltin(SpawnContext & ctx)
       chroot.  */
    ctx.program = canonPath(ctx.program, true);
    if(!isInStore(ctx.program))
        throw Error(format("derivation builder `%1' is outside the store") % ctx.program);
        throw Error(std::format("derivation builder `{}' is outside the store", ctx.program));
    /* If DRV targets the same operating system kernel, try to execute it:
       there might be binfmt_misc set up for user-land emulation of other
       architectures.  However, if it targets a different operating


@@ 2450,13 2449,13 @@ void DerivationGoal::execBuilderOrBuiltin(SpawnContext & ctx)
       that invoke QEMU.  */
    if (error == ENOEXEC && !canBuildLocally(drv.platform)) {
        if (settings.printBuildTrace)
            printMsg(lvlError, format("@ unsupported-platform %1% %2%") % drvPath % drv.platform);
        throw Error(format("a `%1%' is required to build `%3%', but I am a `%2%'")
                    % drv.platform % settings.thisSystem % drvPath);
            printMsg(lvlError, std::format("@ unsupported-platform {} {}", drvPath, drv.platform));
        throw Error(std::format("a `{}' is required to build `{}', but I am a `{}'",
                    drv.platform, settings.thisSystem, drvPath));
    }

    errno = error;
    throw SysError(format("executing `%1%'") % drv.builder);
    throw SysError(std::format("executing `{}'", drv.builder));
}




@@ 2468,13 2467,14 @@ void execBuilderOrBuiltinAction(SpawnContext & ctx)

void DerivationGoal::startBuilder()
{
    auto f = format(
        buildMode == bmRepair ? "repairing path(s) %1%" :
        buildMode == bmCheck ? "checking path(s) %1%" :
        nrRounds > 1 ? "building path(s) %1% (round %2%/%3%)" :
        "building path(s) %1%");
    f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
    startNest(nest, lvlInfo, f % showPaths(missingPaths) % curRound % nrRounds);
    auto path = showPaths(missingPaths);
    auto f = std::vformat(
        buildMode == bmRepair ? "repairing path(s) {0}" :
        buildMode == bmCheck ? "checking path(s) {0}" :
        nrRounds > 1 ? "building path(s) {0} (round {1}/{2})" :
        "building path(s) {0}",
        std::make_format_args(path, curRound, nrRounds));
    startNest(nest, lvlInfo, f);

    /* A ChrootBuildSpawnContext reference can be passed to procedures
       expecting a SpawnContext reference */


@@ 2558,7 2558,7 @@ void DerivationGoal::startBuilder()
    ctx.env["NIX_STORE"] = settings.nixStore;

    /* The maximum number of cores to utilize for parallel building. */
    ctx.env["NIX_BUILD_CORES"] = (format("%d") % settings.buildCores).str();
    ctx.env["NIX_BUILD_CORES"] = std::format("{}", settings.buildCores);

    /* Add all bindings specified in the derivation. */
    for (auto& i : drv.env)


@@ 2630,7 2630,7 @@ void DerivationGoal::startBuilder()
    string s = get(drv.env, "exportReferencesGraph");
    Strings ss = tokenizeString<Strings>(s);
    if (ss.size() % 2 != 0)
        throw BuildError(format("odd number of tokens in `exportReferencesGraph': `%1%'") % s);
        throw BuildError(std::format("odd number of tokens in `exportReferencesGraph': `{}'", s));
    for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
        string fileName = *i++;
        checkStoreName(fileName); /* !!! abuse of this function */


@@ 2638,12 2638,12 @@ void DerivationGoal::startBuilder()
        /* Check that the store path is valid. */
        Path storePath = *i++;
        if (!isInStore(storePath))
            throw BuildError(format("`exportReferencesGraph' contains a non-store path `%1%'")
                % storePath);
            throw BuildError(std::format("`exportReferencesGraph' contains a non-store path `{}'",
                storePath));
        storePath = toStorePath(storePath);
        if (!worker.store.isValidPath(storePath))
            throw BuildError(format("`exportReferencesGraph' contains an invalid path `%1%'")
                % storePath);
            throw BuildError(std::format("`exportReferencesGraph' contains an invalid path `{}'",
                storePath));

        /* If there are derivations in the graph, then include their
           outputs as well.  This is useful if you want to do things


@@ 2680,7 2680,7 @@ void DerivationGoal::startBuilder()

        /* Change ownership of the temporary build directory. */
        if (chown(tmpDir.c_str(), buildUser.getUID(), buildUser.getGID()) == -1)
            throw SysError(format("cannot change ownership of '%1%'") % tmpDir);
            throw SysError(std::format("cannot change ownership of '{}'", tmpDir));

        ctx.setuid = true;
        ctx.user = buildUser.getUID();


@@ 2711,7 2711,7 @@ void DerivationGoal::startBuilder()

        if(fixedOutput) {
            if(findProgram(settings.slirp4netns) == "")
                printMsg(lvlError, format("`%1%' can't be found in PATH, network access disabled") % settings.slirp4netns);
                printMsg(lvlError, std::format("`{}' can't be found in PATH, network access disabled", settings.slirp4netns));
            else {
                if(!pathExists("/dev/net/tun"))
                    printMsg(lvlError, "`/dev/net/tun' is missing, network access disabled");


@@ 2737,15 2737,15 @@ void DerivationGoal::startBuilder()
        ctx.hostname = "localhost";
        ctx.domainname = "(none)";  /* kernel default */

        printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % chrootRootDir);
        printMsg(lvlChatty, std::format("setting up chroot environment in `{}'", chrootRootDir));

	if (mkdir(chrootRootTop.c_str(), 0750) == -1)
	    throw SysError(format("cannot create build root container '%1%'") % chrootRootTop);
	    throw SysError(std::format("cannot create build root container '{}'", chrootRootTop));
        if (mkdir(chrootRootDir.c_str(), 0750) == -1)
            throw SysError(format("cannot create build root '%1%'") % chrootRootDir);
            throw SysError(std::format("cannot create build root '{}'", chrootRootDir));

        if (buildUser.enabled() && chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1)
            throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir);
            throw SysError(std::format("cannot change ownership of `{}'", chrootRootDir));

        /* Create a writable /tmp in the chroot.  Many builders need
           this.  (Of course they should really respect $TMPDIR


@@ 2760,17 2760,17 @@ void DerivationGoal::startBuilder()
        createDirs(chrootRootDir + "/etc");

        writeFile(chrootRootDir + "/etc/passwd",
            (format(
                "nixbld:x:%1%:%2%:Nix build user:/:/noshell\n"
                "nobody:x:65534:65534:Nobody:/:/noshell\n")
                % (buildUser.enabled() ? buildUser.getUID() : guestUID)
                % (buildUser.enabled() ? buildUser.getGID() : guestGID)).str());
            std::format(
                "nixbld:x:{}:{}:Nix build user:/:/noshell\n"
                "nobody:x:65534:65534:Nobody:/:/noshell\n",
                buildUser.enabled() ? buildUser.getUID() : guestUID,
                buildUser.enabled() ? buildUser.getGID() : guestGID));

        /* Declare the build user's group so that programs get a consistent
           view of the system (e.g., "id -gn"). */
        writeFile(chrootRootDir + "/etc/group",
            (format("nixbld:!:%1%:\n")
                % (buildUser.enabled() ? buildUser.getGID() : guestGID)).str());
            std::format("nixbld:!:{}:\n",
                buildUser.enabled() ? buildUser.getGID() : guestGID));

        if (fixedOutput) {
            /* Fixed-output derivations typically need to access the network,


@@ 2834,7 2834,7 @@ void DerivationGoal::startBuilder()
        if (buildUser.enabled() && chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1)
	     /* As an extra security precaution, make the fake store only
		writable by the build user.  */
	     throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir);
	     throw SysError(std::format("cannot change ownership of `{}'", chrootStoreDir));

        /* Make the closure of the inputs available in the chroot, rather than
           the whole store.  This prevents any access to undeclared


@@ 2902,7 2902,7 @@ void DerivationGoal::startBuilder()
        ctx.phases = getBasicSpawnPhases();

        if (pathExists(homeDir))
            throw Error(format("directory `%1%' exists; please remove it") % homeDir);
            throw Error(std::format("directory `{}' exists; please remove it", homeDir));

        /* We're not doing a chroot build, but we have some valid
           output paths.  Since we can't just overwrite or delete


@@ 2930,7 2930,7 @@ void DerivationGoal::startBuilder()
    replacePhase(ctx.phases, "exec", execBuilderOrBuiltinAction);

    /* Run the builder. */
    printMsg(lvlChatty, format("executing builder `%1%'") % drv.builder);
    printMsg(lvlChatty, std::format("executing builder `{}'", drv.builder));

    /* Create the log file. */
    Path logFile = openLogFile();


@@ 3070,8 3070,8 @@ void DerivationGoal::startBuilder()
    if (!msg.empty()) throw Error(msg);

    if (settings.printBuildTrace) {
        printMsg(lvlError, format("@ build-started %1% - %2% %3% %4%")
                 % drvPath % drv.platform % logFile % pid);
        printMsg(lvlError, std::format("@ build-started {} - {} {} {}",
            drvPath, drv.platform, logFile, pid_t(pid)));
    }

}


@@ 3090,8 3090,7 @@ PathSet parseReferenceSpecifiers(const Derivation & drv, string attr)
        else if (drv.outputs.find(i) != drv.outputs.end())
            result.insert(drv.outputs.find(i)->second.path);
        else throw BuildError(
            format("derivation contains an invalid reference specifier `%1%'")
            % i);
            std::format("derivation contains an invalid reference specifier `{}'", i));
    }
    return result;
}


@@ 3142,9 3141,9 @@ void DerivationGoal::registerOutputs()
        if (lstat(actualPath.c_str(), &st) == -1) {
            if (errno == ENOENT)
                throw BuildError(
                    format("builder for `%1%' failed to produce output path `%2%'")
                    % drvPath % path);
            throw SysError(format("getting attributes of path `%1%'") % actualPath);
                    std::format("builder for `{}' failed to produce output path `{}'",
                        drvPath, path));
            throw SysError(std::format("getting attributes of path `{}'", actualPath));
        }

#ifndef __CYGWIN__


@@ 3154,13 3153,13 @@ void DerivationGoal::registerOutputs()
           user. */
        if ((!S_ISLNK(st.st_mode) && (st.st_mode & (S_IWGRP | S_IWOTH))) ||
            (buildUser.enabled() && st.st_uid != buildUser.getUID()))
            throw BuildError(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path);
            throw BuildError(std::format("suspicious ownership or permission on `{}'; rejecting this build output", path));
#endif

        /* Apply hash rewriting if necessary. */
        bool rewritten = false;
        if (!rewritesFromTmp.empty()) {
            printMsg(lvlError, format("warning: rewriting hashes in `%1%'; cross fingers") % path);
            printMsg(lvlError, std::format("warning: rewriting hashes in `{}'; cross fingers", path));

            /* Canonicalise first.  This ensures that the path we're
               rewriting doesn't contain a hard link to /etc/shadow or


@@ 3179,7 3178,7 @@ void DerivationGoal::registerOutputs()
        }

        startNest(nest, lvlTalkative,
            format("scanning for references inside `%1%'") % path);
            std::format("scanning for references inside `{}'", path));

        /* Check that fixed-output derivations produced the right
           outputs (i.e., the content hash should match the specified


@@ 3194,17 3193,17 @@ void DerivationGoal::registerOutputs()
                   execute permission. */
                if (!S_ISREG(st.st_mode) || (st.st_mode & S_IXUSR) != 0)
                    throw BuildError(
                        format("output path `%1% should be a non-executable regular file") % path);
                        std::format("output path `{}' should be a non-executable regular file", path));
            }

            /* Check the hash. */
            Hash h2 = recursive ? hashPath(ht, actualPath).first : hashFile(ht, actualPath);
            if (h != h2) {
		if (settings.printBuildTrace)
		    printMsg(lvlError, format("@ hash-mismatch %1% %2% %3% %4%")
			     % path % i.second.hashAlgo
			     % printHash16or32(h) % printHash16or32(h2));
                throw BuildError(format("hash mismatch for store item '%1%'") % path);
		    printMsg(lvlError, std::format("@ hash-mismatch {} {} {} {}",
			    path, i.second.hashAlgo,
                printHash16or32(h), printHash16or32(h2)));
                throw BuildError(std::format("hash mismatch for store item '{}'", path));
	    }
        }



@@ 3224,16 3223,16 @@ void DerivationGoal::registerOutputs()
		if (buildMode != bmCheck) {
		    if (S_ISDIR(st.st_mode)) {
                        if (lstat(actualPath.c_str(), &st) == -1)
                            throw SysError(format("getting canonicalized permissions of directory `%1%'") % actualPath);
                            throw SysError(std::format("getting canonicalized permissions of directory `{}'", actualPath));
			/* Change mode on the directory to allow for
			   rename(2).  */
			if (chmod(actualPath.c_str(), st.st_mode | 0700) == -1)
                            throw SysError(format("making `%1%' writable for move from chroot to store") % actualPath);
                            throw SysError(std::format("making `{}' writable for move from chroot to store", actualPath));
                    }
		    if (rename(actualPath.c_str(), path.c_str()) == -1)
			throw SysError(format("moving build output `%1%' from the chroot to the store") % path);
			throw SysError(std::format("moving build output `{}' from the chroot to the store", path));
		    if (S_ISDIR(st.st_mode) && chmod(path.c_str(), st.st_mode) == -1)
			throw SysError(format("restoring permissions on directory `%1%'") % actualPath);
			throw SysError(std::format("restoring permissions on directory `{}'", actualPath));
		}
          }
          if (buildMode != bmCheck) actualPath = path;


@@ 3254,16 3253,16 @@ void DerivationGoal::registerOutputs()
                    Path dst = path + checkSuffix;
                    if (pathExists(dst)) deletePath(dst);
                    if (rename(actualPath.c_str(), dst.c_str()))
                        throw SysError(format("renaming `%1%' to `%2%'") % actualPath % dst);
                    throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs from `%3%'")
                        % drvPath % path % dst);
                        throw SysError(std::format("renaming `{}' to `{}'", actualPath, dst));
                    throw Error(std::format("derivation `{}' may not be deterministic: output `{}' differs from `{}'",
                        drvPath, path, dst));
                } else
                    throw Error(format("derivation `%1%' may not be deterministic: output `%2%' differs")
                        % drvPath % path);
                    throw Error(std::format("derivation `{}' may not be deterministic: output `{}' differs",
                        drvPath, path));
            }

            if (settings.printBuildTrace)
                printMsg(lvlError, format("@ build-succeeded %1% -") % drvPath);
                printMsg(lvlError, std::format("@ build-succeeded {} -", drvPath));

            continue;
        }


@@ 3273,9 3272,9 @@ void DerivationGoal::registerOutputs()
        for (auto& i : inputPaths) {
            PathSet::iterator j = references.find(i);
            if (j == references.end())
                debug(format("unreferenced input: `%1%'") % i);
                debug(std::format("unreferenced input: `{}'", i));
            else
                debug(format("referenced input: `%1%'") % i);
                debug(std::format("referenced input: `{}'", i));
        }

        /* Enforce `allowedReferences' and friends. */


@@ 3297,10 3296,10 @@ void DerivationGoal::registerOutputs()
            for (auto & i : used)
                if (allowed) {
                    if (spec.find(i) == spec.end())
                        throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath % i);
                        throw BuildError(std::format("output (`{}') is not allowed to refer to path `{}'", actualPath, i));
                } else {
                    if (spec.find(i) != spec.end())
                        throw BuildError(format("output (`%1%') is not allowed to refer to path `%2%'") % actualPath % i);
                        throw BuildError(std::format("output (`{}') is not allowed to refer to path `{}'", actualPath, i));
                }
        };



@@ 3333,12 3332,12 @@ void DerivationGoal::registerOutputs()
                Path prev = i->path + checkSuffix;
                if (pathExists(prev))
                    throw NotDeterministic(
                        format("output ‘%1%’ of ‘%2%’ differs from ‘%3%’ from previous round")
                        % i->path % drvPath % prev);
                        std::format("output `{}' of `{}' differs from `{}' from previous round",
                            i->path, drvPath, prev));
                else
                    throw NotDeterministic(
                        format("output ‘%1%’ of ‘%2%’ differs from previous round")
                        % i->path % drvPath);
                        std::format("output `{}' of `{}' differs from previous round",
                            i->path, drvPath));
            }
        assert(false); // shouldn't happen
    }


@@ 3350,7 3349,7 @@ void DerivationGoal::registerOutputs()
            if (curRound < nrRounds) {
                Path dst = i.second.path + checkSuffix;
                if (rename(i.second.path.c_str(), dst.c_str()))
                    throw SysError(format("renaming ‘%1%’ to ‘%2%’") % i.second.path % dst);
                    throw SysError(std::format("renaming `{}' to `{}'", i.second.path, dst));
            }
        }



@@ 3380,20 3379,20 @@ Path DerivationGoal::openLogFile()
    string baseName = baseNameOf(drvPath);

    /* Create a log file. */
    Path dir = (format("%1%/%2%/%3%/") % settings.nixLogDir % drvsLogDir % string(baseName, 0, 2)).str();
    Path dir = std::format("{}/{}/{}/", settings.nixLogDir, drvsLogDir, string(baseName, 0, 2));
    createDirs(dir);

    switch (settings.logCompression)
      {
      case COMPRESSION_GZIP: {
        Path logFileName = (format("%1%/%2%.gz") % dir % string(baseName, 2)).str();
        Path logFileName = std::format("{}/{}.gz", dir, string(baseName, 2));
        AutoCloseFD fd = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
        if (fd == -1) throw SysError(format("creating log file `%1%'") % logFileName);
        if (fd == -1) throw SysError(std::format("creating log file `{}'", logFileName));
        closeOnExec(fd);

	/* Note: FD will be closed by 'gzclose'.  */
        if (!(gzLogFile = gzdopen(fd.borrow(), "w")))
            throw Error(format("cannot open compressed log file `%1%'") % logFileName);
            throw Error(std::format("cannot open compressed log file `{}'", logFileName));

        gzbuffer(gzLogFile, 32768);
        gzsetparams(gzLogFile, Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY);


@@ 3403,26 3402,26 @@ Path DerivationGoal::openLogFile()

#if HAVE_BZLIB_H
      case COMPRESSION_BZIP2: {
        Path logFileName = (format("%1%/%2%.bz2") % dir % string(baseName, 2)).str();
        Path logFileName = std::format("{}/{}.bz2", dir, string(baseName, 2));
        AutoCloseFD fd = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
        if (fd == -1) throw SysError(format("creating log file `%1%'") % logFileName);
        if (fd == -1) throw SysError(std::format("creating log file `{}'", logFileName));
        closeOnExec(fd);

        if (!(fLogFile = fdopen(fd.borrow(), "w")))
            throw SysError(format("opening log file `%1%'") % logFileName);
            throw SysError(std::format("opening log file `{}'", logFileName));

        int err;
        if (!(bzLogFile = BZ2_bzWriteOpen(&err, fLogFile, 9, 0, 0)))
            throw Error(format("cannot open compressed log file `%1%'") % logFileName);
            throw Error(std::format("cannot open compressed log file `{}'", logFileName));

        return logFileName;
      }
#endif

      case COMPRESSION_NONE: {
        Path logFileName = (format("%1%/%2%") % dir % string(baseName, 2)).str();
        Path logFileName = std::format("{}/{}", dir, string(baseName, 2));
        fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
        if (fdLogFile == -1) throw SysError(format("creating log file `%1%'") % logFileName);
        if (fdLogFile == -1) throw SysError(std::format("creating log file `{}'", logFileName));
        closeOnExec(fdLogFile);
        return logFileName;
      }


@@ 3438,14 3437,14 @@ void DerivationGoal::closeLogFile()
	int err;
	err = gzclose(gzLogFile);
	gzLogFile = NULL;
	if (err != Z_OK) throw Error(format("cannot close compressed log file (gzip error = %1%)") % err);
	if (err != Z_OK) throw Error(std::format("cannot close compressed log file (gzip error = {})", err));
    }
#if HAVE_BZLIB_H
    else if (bzLogFile) {
        int err;
        BZ2_bzWriteClose(&err, bzLogFile, 0, 0, 0);
        bzLogFile = 0;
        if (err != BZ_OK) throw Error(format("cannot close compressed log file (BZip2 error = %1%)") % err);
        if (err != BZ_OK) throw Error(std::format("cannot close compressed log file (BZip2 error = {})", err));
    }
#endif



@@ 3463,7 3462,7 @@ static void _chown(const Path & path, uid_t uid, gid_t gid)
    checkInterrupt();

    if (lchown(path.c_str(), uid, gid) == -1) {
	throw SysError(format("change owner and group of `%1%'") % path);
	throw SysError(std::format("change owner and group of `{}'", path));
    }
    struct stat st = lstat(path);
    if (S_ISDIR(st.st_mode)) {


@@ 3486,8 3485,7 @@ void DerivationGoal::deleteTmpDir(bool force)

        if (settings.keepFailed && !force) {
            printMsg(lvlError,
                format("note: keeping build directory `%2%'")
                % drvPath % top);
                std::format("note: keeping build directory `{}'", top));
            chmod(tmpDir.c_str(), 0755);

            // Change the ownership if clientUid is set. Never change the


@@ 3516,8 3514,8 @@ void DerivationGoal::deleteTmpDir(bool force)
		    /* When running as an unprivileged user and without
		       CAP_CHOWN, we cannot chown the build tree.  Print a
		       message and keep going.  */
		    printMsg(lvlInfo, format("cannot change ownership of build directory '%1%': %2%")
			     % tmpDir % strerror(e.errNo));
		    printMsg(lvlInfo, std::format("cannot change ownership of build directory '{}': {}",
			    tmpDir, strerror(e.errNo)));
		}

		if (top != tmpDir) {


@@ 3568,8 3566,8 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
        logSize += data.size();
        if (settings.maxLogSize && logSize > settings.maxLogSize) {
            printMsg(lvlError,
                format("%1% killed after writing more than %2% bytes of log output")
                % getName() % settings.maxLogSize);
                std::format("{} killed after writing more than {} bytes of log output",
                getName(), settings.maxLogSize));
            timedOut(); // not really a timeout, but close enough
            return;
        }


@@ 3580,13 3578,13 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
	    if (data.size() > 0) {
		int count, err;
		count = gzwrite(gzLogFile, data.data(), data.size());
		if (count == 0) throw Error(format("cannot write to compressed log file (gzip error = %1%)") % gzerror(gzLogFile, &err));
		if (count == 0) throw Error(std::format("cannot write to compressed log file (gzip error = {})", gzerror(gzLogFile, &err)));
	    }
#if HAVE_BZLIB_H
	} else if (bzLogFile) {
            int err;
            BZ2_bzWrite(&err, bzLogFile, (unsigned char *) data.data(), data.size());
            if (err != BZ_OK) throw Error(format("cannot write to compressed log file (BZip2 error = %1%)") % err);
            if (err != BZ_OK) throw Error(std::format("cannot write to compressed log file (BZip2 error = {})", err));
#endif
        } else if (fdLogFile != -1)
            writeFull(fdLogFile, data);


@@ 3623,10 3621,10 @@ bool DerivationGoal::pathFailed(const Path & path)

    if (!worker.store.hasPathFailed(path)) return false;

    printMsg(lvlError, format("builder for `%1%' failed previously (cached)") % path);
    printMsg(lvlError, std::format("builder for `{}' failed previously (cached)", path));

    if (settings.printBuildTrace)
        printMsg(lvlError, format("@ build-failed %1% - cached") % drvPath);
        printMsg(lvlError, std::format("@ build-failed {} - cached", drvPath));

    done(BuildResult::CachedFailure);



@@ 3644,8 3642,8 @@ Path DerivationGoal::addHashRewrite(const Path & path)
    rewritesToTmp[h1] = h2;
    rewritesFromTmp[h2] = h1;
    redirectedOutputs[path] = p;
    printMsg(lvlChatty, format("output '%1%' redirected to '%2%'")
	     % path % p);
    printMsg(lvlChatty, std::format("output '{}' redirected to '{}'",
	    path, p));
    return p;
}



@@ 3734,7 3732,7 @@ SubstitutionGoal::SubstitutionGoal(const Path & storePath, Worker & worker, bool
{
    this->storePath = storePath;
    state = &SubstitutionGoal::init;
    name = (format("substitution of `%1%'") % storePath).str();
    name = std::format("substitution of `{}'", storePath);
    trace("created");
}



@@ 3748,7 3746,7 @@ SubstitutionGoal::~SubstitutionGoal()
void SubstitutionGoal::timedOut()
{
    if (settings.printBuildTrace)
        printMsg(lvlError, format("@ substituter-failed %1% timeout") % storePath);
        printMsg(lvlError, std::format("@ substituter-failed {} timeout", storePath));
    if (substituter) {
        pid_t savedPid = substituter->pid;
	substituter.reset();


@@ 3777,7 3775,7 @@ void SubstitutionGoal::init()
    }

    if (settings.readOnlyMode)
        throw Error(format("cannot substitute path `%1%' - no write access to the store") % storePath);
        throw Error(std::format("cannot substitute path `{}' - no write access to the store", storePath));

    tryNext();
}


@@ 3794,7 3792,7 @@ void SubstitutionGoal::tryNext()
    if (k == infos.end()) {
        /* None left.  Terminate this goal and let someone else deal
           with it. */
        debug(format("path `%1%' is required, but there is no substituter that can build it") % storePath);
        debug(std::format("path `{}' is required, but there is no substituter that can build it", storePath));
        /* Hack: don't indicate failure if there were no substituters.
           In that case the calling derivation should just do a
           build. */


@@ 3823,7 3821,7 @@ void SubstitutionGoal::referencesValid()
    trace("all references realised");

    if (nrFailed > 0) {
        debug(format("some references of path `%1%' could not be realised") % storePath);
        debug(std::format("some references of path `{}' could not be realised", storePath));
        amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed);
        return;
    }


@@ 3855,8 3853,8 @@ void SubstitutionGoal::tryToRun()
       first, but let's be defensive). */
    outputLock.reset(); // make sure this goal's lock is gone
    if (pathIsLockedByMe(storePath)) {
        debug(format("restarting substitution of `%1%' because it's locked by another goal")
            % storePath);
        debug(std::format("restarting substitution of `{}' because it's locked by another goal",
            storePath));
        worker.waitForAnyGoal(shared_from_this());
        return; /* restart in the tryToRun() state when another goal finishes */
    }


@@ 3870,14 3868,14 @@ void SubstitutionGoal::tryToRun()

    /* Check again whether the path is invalid. */
    if (!repair && worker.store.isValidPath(storePath)) {
        debug(format("store path `%1%' has become valid") % storePath);
        debug(std::format("store path `{}' has become valid", storePath));
        outputLock->setDeletion(true);
        outputLock.reset();
        amDone(ecSuccess);
        return;
    }

    printMsg(lvlInfo, format("fetching path `%1%'...") % storePath);
    printMsg(lvlInfo, std::format("fetching path `{}'...", storePath));

    destPath = repair ? storePath + ".tmp" : storePath;



@@ 3901,7 3899,7 @@ void SubstitutionGoal::tryToRun()

    /* Send the request to the substituter.  */
    writeLine(substituter->toAgent.writeSide,
	      (format("substitute %1% %2%") % storePath % destPath).str());
	    std::format("substitute {} {}", storePath, destPath));

    set<int> fds;
    fds.insert(substituter->fromAgent.readSide);


@@ 3913,7 3911,7 @@ void SubstitutionGoal::tryToRun()
    if (settings.printBuildTrace)
	/* The second element in the message used to be the name of the
	   substituter but we're left with only one.  */
        printMsg(lvlError, format("@ substituter-started %1% substitute") % storePath);
        printMsg(lvlError, std::format("@ substituter-started {} substitute", storePath));
}




@@ 3939,35 3937,33 @@ void SubstitutionGoal::finished()
	auto statusList = tokenizeString<vector<string> >(status);

	if (statusList.empty()) {
            throw SubstError(format("fetching path `%1%' (empty status)")
			     % storePath);
            throw SubstError(std::format("fetching path `{}' (empty status)", storePath));
	} else if (statusList[0] == "hash-mismatch") {
	    if (settings.printBuildTrace) {
		auto hashType = statusList[1];
		auto expectedHash = statusList[2];
		auto actualHash = statusList[3];
		printMsg(lvlError, format("@ hash-mismatch %1% %2% %3% %4%")
			 % storePath
			 % hashType % expectedHash % actualHash);
		printMsg(lvlError, std::format("@ hash-mismatch {} {} {} {}",
		    storePath, hashType, expectedHash, actualHash));
	    }
	    throw SubstError(format("hash mismatch for substituted item `%1%'") % storePath);
	    throw SubstError(std::format("hash mismatch for substituted item `{}'", storePath));
	} else if (statusList[0] == "success") {
	    if (!pathExists(destPath))
		throw SubstError(format("substitute did not produce path `%1%'") % destPath);
		throw SubstError(std::format("substitute did not produce path `{}'", destPath));

	    std::string hashStr = statusList[1];
	    size_t n = hashStr.find(':');
	    if (n == string::npos)
		throw Error(format("bad hash from substituter: %1%") % hashStr);
		throw Error(std::format("bad hash from substituter: {}", hashStr));

	    HashType hashType = parseHashType(string(hashStr, 0, n));
	    switch (hashType) {
	    case htUnknown:
		throw Error(format("unknown hash algorithm in `%1%'") % hashStr);
		throw Error(std::format("unknown hash algorithm in `{}'", hashStr));
	    case htSHA256:
		hash.first = parseHash16or32(hashType, string(hashStr, n + 1));
		if (!string2Int(statusList[2], hash.second))
		    throw Error(format("invalid nar size for '%1%' substitute") % storePath);
		    throw Error(std::format("invalid nar size for '{}' substitute", storePath));
		break;
	    default:
		/* The database only stores SHA256 hashes, so compute it.  */


@@ 3976,16 3972,16 @@ void SubstitutionGoal::finished()
	    }
	}
	else
            throw SubstError(format("fetching path `%1%' (status: '%2%')")
                % storePath % status);
            throw SubstError(std::format("fetching path `{}' (status: '{}')",
                storePath, status));

    } catch (SubstError & e) {

        printMsg(lvlInfo, e.msg());

        if (settings.printBuildTrace) {
            printMsg(lvlError, format("@ substituter-failed %1% %2% %3%")
                % storePath % status % e.msg());
            printMsg(lvlError, std::format("@ substituter-failed {} {} {}",
                storePath, status, e.msg()));
        }

	amDone(ecFailed);


@@ 4011,10 4007,10 @@ void SubstitutionGoal::finished()
    worker.store.markContentsGood(storePath);

    printMsg(lvlChatty,
        format("substitution of path `%1%' succeeded") % storePath);
        std::format("substitution of path `{}' succeeded", storePath));

    if (settings.printBuildTrace)
        printMsg(lvlError, format("@ substituter-succeeded %1%") % storePath);
        printMsg(lvlError, std::format("@ substituter-succeeded {}", storePath));

    amDone(ecSuccess);
}


@@ 4042,7 4038,7 @@ void SubstitutionGoal::handleChildOutput(int fd, const string & data)
		status = trimmed;
		worker.wakeUp(shared_from_this());
	    } else {
		printMsg(lvlError, format("unexpected substituter message '%1%'") % input);
		printMsg(lvlError, std::format("unexpected substituter message '{}'", input));
	    }

	    input = (end != string::npos) ? input.substr(end + 1) : "";


@@ 4234,7 4230,7 @@ void Worker::run(const Goals & _topGoals)
{
    for (auto& i :  _topGoals) topGoals.insert(i);

    startNest(nest, lvlDebug, format("entered goal loop"));
    startNest(nest, lvlDebug, "entered goal loop");

    while (1) {



@@ 4308,7 4304,7 @@ void Worker::waitForInput()
    if (nearest != LONG_MAX) {
        timeout.tv_sec = std::max((time_t) 1, nearest - before);
        useTimeout = true;
        printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
        printMsg(lvlVomit, std::format("sleeping {} seconds", timeout.tv_sec));
    }

    /* If we are polling goals that are waiting for a lock, then wake


@@ 4365,15 4361,14 @@ void Worker::waitForInput()
                ssize_t rd = read(k, buffer, sizeof(buffer));
                if (rd == -1) {
                    if (errno != EINTR)
                        throw SysError(format("reading from %1%")
                            % goal->getName());
                        throw SysError(std::format("reading from {}", goal->getName()));
                } else if (rd == 0) {
                    debug(format("%1%: got EOF") % goal->getName());
                    debug(std::format("{}: got EOF", goal->getName()));
                    goal->handleEOF(k);
                    j->second.fds.erase(k);
                } else {
                    printMsg(lvlVomit, format("%1%: read %2% bytes")
                        % goal->getName() % rd);
                    printMsg(lvlVomit, std::format("{}: read {} bytes",
                        goal->getName(), rd));
                    string data((char *) buffer, rd);
                    j->second.lastOutput = after;
                    goal->handleChildOutput(k, data);


@@ 4387,8 4382,8 @@ void Worker::waitForInput()
            after - j->second.lastOutput >= (time_t) settings.maxSilentTime)
        {
            printMsg(lvlError,
                format("%1% timed out after %2% seconds of silence")
                % goal->getName() % settings.maxSilentTime);
                std::format("{} timed out after {} seconds of silence",
                goal->getName(), settings.maxSilentTime));
            goal->timedOut();
        }



@@ 4398,8 4393,8 @@ void Worker::waitForInput()
            after - j->second.timeStarted >= (time_t) settings.buildTimeout)
        {
            printMsg(lvlError,
                format("%1% timed out after %2% seconds")
                % goal->getName() % settings.buildTimeout);
                std::format("{} timed out after %2% seconds",
                goal->getName(), settings.buildTimeout));
            goal->timedOut();
        }
    }


@@ 4427,7 4422,7 @@ unsigned int Worker::exitStatus()
void LocalStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode)
{
    startNest(nest, lvlDebug,
        format("building %1%") % showPaths(drvPaths));
        std::format("building {}", showPaths(drvPaths)));

    Worker worker(*this);



@@ 4451,7 4446,7 @@ void LocalStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode)
        }

    if (!failed.empty())
        throw Error(format("build of %1% failed") % showPaths(failed), worker.exitStatus());
        throw Error(std::format("build of {} failed", showPaths(failed)), worker.exitStatus());
}




@@ 4467,7 4462,7 @@ void LocalStore::ensurePath(const Path & path)
    worker.run(goals);

    if (goal->getExitCode() != Goal::ecSuccess)
        throw Error(format("path `%1%' does not exist and cannot be created") % path, worker.exitStatus());
        throw Error(std::format("path `{}' does not exist and cannot be created", path), worker.exitStatus());
}




@@ 4488,7 4483,7 @@ void LocalStore::repairPath(const Path & path)
            goals.insert(worker.makeDerivationGoal(deriver, StringSet(), bmRepair));
            worker.run(goals);
        } else
            throw Error(format("cannot repair path `%1%'") % path, worker.exitStatus());
            throw Error(std::format("cannot repair path `{}'", path), worker.exitStatus());
    }
}


M nix/libstore/builtins.cc => nix/libstore/builtins.cc +2 -1
@@ 22,6 22,7 @@

#include <unistd.h>
#include <cstdlib>
#include <format>

namespace nix {



@@ 53,7 54,7 @@ static void builtinDownload(const Derivation &drv,
    const string program = settings.guixProgram;
    execv(program.c_str(), (char *const *) argv);

    throw SysError(format("failed to run download program '%1%'") % program);
    throw SysError(std::format("failed to run download program '{}'", program));
}

static const std::map<std::string, derivationBuilder> builtins =

M nix/libstore/builtins.hh => nix/libstore/builtins.hh +0 -1
@@ 21,7 21,6 @@
#pragma once

#include <derivations.hh>
#include <map>
#include <string>

namespace nix {

M nix/libstore/derivations.cc => nix/libstore/derivations.cc +6 -3
@@ 4,6 4,9 @@
#include "util.hh"
#include "misc.hh"

#include <format>

#include <cassert>

namespace nix {



@@ 20,7 23,7 @@ void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash

    hashType = parseHashType(algo);
    if (hashType == htUnknown)
        throw Error(format("unknown hash algorithm `%1%'") % algo);
        throw Error(std::format("unknown hash algorithm `{}'", algo));

    hash = parseHash(hashType, this->hash);
}


@@ 48,7 51,7 @@ static Path parsePath(std::istream & str)
{
    string s = parseString(str);
    if (s.size() == 0 || s[0] != '/')
        throw FormatError(format("bad path `%1%' in derivation") % s);
        throw FormatError(std::format("bad path `{}' in derivation", s));
    return s;
}



@@ 117,7 120,7 @@ Derivation readDerivation(const Path & drvPath)
    try {
        return parseDerivation(readFile(drvPath));
    } catch (FormatError & e) {
        throw Error(format("error parsing derivation `%1%': %2%") % drvPath % e.msg());
        throw Error(std::format("error parsing derivation `{}': {}", drvPath, e.msg()));
    }
}


M nix/libstore/gc.cc => nix/libstore/gc.cc +68 -71
@@ 6,6 6,7 @@
#include <queue>
#include <random>
#include <algorithm>
#include <format>

#include <sys/types.h>
#include <sys/stat.h>


@@ 30,18 31,17 @@ static string gcRootsDir = "gcroots";
   yielded the GC lock. */
int LocalStore::openGCLock(LockType lockType)
{
    Path fnGCLock = (format("%1%/%2%")
        % settings.nixStateDir % gcLockName).str();
    Path fnGCLock = std::format("{}/{}", settings.nixStateDir, gcLockName);

    debug(format("acquiring global GC lock `%1%'") % fnGCLock);
    debug(std::format("acquiring global GC lock `{}'", fnGCLock));

    AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600);
    if (fdGCLock == -1)
        throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
        throw SysError(std::format("opening global GC lock `{}'", fnGCLock));
    closeOnExec(fdGCLock);

    if (!lockFile(fdGCLock, lockType, false)) {
        printMsg(lvlError, format("waiting for the big garbage collector lock..."));
        printMsg(lvlError, "waiting for the big garbage collector lock...");
        lockFile(fdGCLock, lockType, true);
    }



@@ 59,14 59,12 @@ static void makeSymlink(const Path & link, const Path & target)
    createDirs(dirOf(link));

    /* Create the new symlink. */
    Path tempLink = (format("%1%.tmp-%2%-%3%")
        % link % getpid() % rand()).str();
    Path tempLink = std::format("{}.tmp-{}-{}", link, getpid(), rand());
    createSymlink(target, tempLink);

    /* Atomically replace the old one. */
    if (rename(tempLink.c_str(), link.c_str()) == -1)
        throw SysError(format("cannot rename `%1%' to `%2%'")
            % tempLink % link);
        throw SysError(std::format("cannot rename `{}' to `{}'", tempLink, link));
}




@@ 79,8 77,8 @@ void LocalStore::syncWithGC()
void LocalStore::addIndirectRoot(const Path & path)
{
    string hash = printHash32(hashString(htSHA1, path));
    Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
        % settings.nixStateDir % gcRootsDir % hash).str());
    Path realRoot = canonPath(std::format("{}/{}/auto/{}",
        settings.nixStateDir, gcRootsDir, hash));
    makeSymlink(realRoot, path);
}



@@ 93,28 91,28 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
    assertStorePath(storePath);

    if (isInStore(gcRoot))
        throw Error(format(
                "creating a garbage collector root (%1%) in the store is forbidden "
                "(are you running nix-build inside the store?)") % gcRoot);
        throw Error(std::format(
                "creating a garbage collector root ({}) in the store is forbidden "
                "(are you running nix-build inside the store?)", gcRoot));

    if (indirect) {
        /* Don't clobber the link if it already exists and doesn't
           point to the store. */
        if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
            throw Error(format("cannot create symlink `%1%'; already exists") % gcRoot);
            throw Error(std::format("cannot create symlink `{}'; already exists", gcRoot));
        makeSymlink(gcRoot, storePath);
        store.addIndirectRoot(gcRoot);
    }

    else {
        if (!allowOutsideRootsDir) {
            Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
            Path rootsDir = canonPath(std::format("{}/{}", settings.nixStateDir, gcRootsDir));

            if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
                throw Error(format(
                    "path `%1%' is not a valid garbage collector root; "
                    "it's not in the directory `%2%'")
                    % gcRoot % rootsDir);
                throw Error(std::format(
                    "path `{}' is not a valid garbage collector root; "
                    "it's not in the directory `{}'",
                    gcRoot, rootsDir));
        }

        if (baseNameOf(gcRoot) == baseNameOf(storePath))


@@ 132,10 130,10 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
        Roots roots = store.findRoots();
        if (roots.find(gcRoot) == roots.end())
            printMsg(lvlError,
                format(
                    "warning: `%1%' is not in a directory where the garbage collector looks for roots; "
                    "therefore, `%2%' might be removed by the garbage collector")
                % gcRoot % storePath);
                std::format(
                    "warning: `{}' is not in a directory where the garbage collector looks for roots; "
                    "therefore, `{}' might be removed by the garbage collector",
                gcRoot, storePath));
    }

    /* Grab the global GC root, causing us to block while a GC is in


@@ 153,11 151,10 @@ void LocalStore::addTempRoot(const Path & path)
    if (fdTempRoots == -1) {

        while (1) {
            Path dir = (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str();
            Path dir = std::format("{}/{}", settings.nixStateDir, tempRootsDir);
            createDirs(dir);

            fnTempRoots = (format("%1%/%2%")
                % dir % getpid()).str();
            fnTempRoots = std::format("{}/{}", dir, getpid());

            AutoCloseFD fdGCLock = openGCLock(ltRead);



@@ 170,14 167,14 @@ void LocalStore::addTempRoot(const Path & path)

            fdGCLock.close();

            debug(format("acquiring read lock on `%1%'") % fnTempRoots);
            debug(std::format("acquiring read lock on `{}'", fnTempRoots));
            lockFile(fdTempRoots, ltRead, true);

            /* Check whether the garbage collector didn't get in our
               way. */
            struct stat st;
            if (fstat(fdTempRoots, &st) == -1)
                throw SysError(format("statting `%1%'") % fnTempRoots);
                throw SysError(std::format("statting `{}'", fnTempRoots));
            if (st.st_size == 0) break;

            /* The garbage collector deleted this file before we could


@@ 189,14 186,14 @@ void LocalStore::addTempRoot(const Path & path)

    /* Upgrade the lock to a write lock.  This will cause us to block
       if the garbage collector is holding our lock. */
    debug(format("acquiring write lock on `%1%'") % fnTempRoots);
    debug(std::format("acquiring write lock on `{}'", fnTempRoots));
    lockFile(fdTempRoots, ltWrite, true);

    string s = path + '\0';
    writeFull(fdTempRoots, s);

    /* Downgrade to a read lock. */
    debug(format("downgrading to read lock on `%1%'") % fnTempRoots);
    debug(std::format("downgrading to read lock on `{}'", fnTempRoots));
    lockFile(fdTempRoots, ltRead, true);
}



@@ 210,17 207,17 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
    /* Read the `temproots' directory for per-process temporary root
       files. */
    DirEntries tempRootFiles = readDirectory(
        (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str());
        std::format("{}/{}", settings.nixStateDir, tempRootsDir));

    for (auto & i : tempRootFiles) {
        Path path = (format("%1%/%2%/%3%") % settings.nixStateDir % tempRootsDir % i.name).str();
        Path path = std::format("{}/{}/{}", settings.nixStateDir, tempRootsDir, i.name);

        debug(format("reading temporary root file `%1%'") % path);
        debug(std::format("reading temporary root file `{}'", path));
        FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666)));
        if (*fd == -1) {
            /* It's okay if the file has disappeared. */
            if (errno == ENOENT) continue;
            throw SysError(format("opening temporary roots file `%1%'") % path);
            throw SysError(std::format("opening temporary roots file `{}'", path));
        }

        /* This should work, but doesn't, for some reason. */


@@ 231,7 228,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
           only succeed if the owning process has died.  In that case
           we don't care about its temporary roots. */
        if (lockFile(*fd, ltWrite, false)) {
            printMsg(lvlError, format("removing stale temporary roots file `%1%'") % path);
            printMsg(lvlError, std::format("removing stale temporary roots file `{}'", path));
            unlink(path.c_str());
            writeFull(*fd, "d");
            continue;


@@ 240,7 237,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
        /* Acquire a read lock.  This will prevent the owning process
           from upgrading to a write lock, therefore it will block in
           addTempRoot(). */
        debug(format("waiting for read lock on `%1%'") % path);
        debug(std::format("waiting for read lock on `{}'", path));
        lockFile(*fd, ltRead, true);

        /* Read the entire file. */


@@ 251,7 248,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)

        while ((end = contents.find((char) 0, pos)) != string::npos) {
            Path root(contents, pos, end - pos);
            debug(format("got temporary root `%1%'") % root);
            debug(std::format("got temporary root `{}'", root));
            assertStorePath(root);
            tempRoots.insert(root);
            pos = end + 1;


@@ 269,7 266,7 @@ static void foundRoot(StoreAPI & store,
    if (store.isValidPath(storePath))
        roots[path] = storePath;
    else
        printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") % path % storePath);
        printMsg(lvlInfo, std::format("skipping invalid root from `{}' to `{}'", path, storePath));
}




@@ 295,7 292,7 @@ static void findRoots(StoreAPI & store, const Path & path, unsigned char type, R
                target = absPath(target, dirOf(path));
                if (!pathExists(target)) {
                    if (isInDir(path, settings.nixStateDir + "/" + gcRootsDir + "/auto")) {
                        printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
                        printMsg(lvlInfo, std::format("removing stale link from `{}' to `{}'", path, target));
                        unlink(path.c_str());
                    }
                } else {


@@ 318,8 315,8 @@ static void findRoots(StoreAPI & store, const Path & path, unsigned char type, R
    catch (SysError & e) {
        /* We only ignore permanent failures. */
        if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR)
            printMsg(lvlInfo, format("cannot read potential root '%1%': %2%")
		     % path % strerror(e.errNo));
            printMsg(lvlInfo, std::format("cannot read potential root '{}': {}",
		        path, strerror(e.errNo)));
        else
            throw;
    }


@@ 342,8 339,8 @@ Roots LocalStore::findRoots()

static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
{
    debug(format("executing `%1% gc --list-busy' to find additional roots")
	  % settings.guixProgram);
    debug(std::format("executing `{} gc --list-busy' to find additional roots",
	    settings.guixProgram));

    const Strings args = { "gc", "--list-busy" };
    string result = runProgram(settings.guixProgram, false, args);


@@ 354,7 351,7 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
        if (isInStore(i)) {
            Path path = toStorePath(i);
            if (roots.find(path) == roots.end() && store.isValidPath(path)) {
                debug(format("got additional root `%1%'") % path);
                debug(std::format("got additional root `{}'", path));
                roots.insert(path);
            }
        }


@@ 424,17 421,17 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
    struct stat st;
    if (lstat(path.c_str(), &st)) {
        if (errno == ENOENT) return;
        throw SysError(format("getting status of %1%") % path);
        throw SysError(std::format("getting status of {}", path));
    }

    if (state.options.maxFreed != ULLONG_MAX) {
	auto freed = state.results.bytesFreed + state.bytesInvalidated;
	double fraction = ((double) freed) / (double) state.options.maxFreed;
	unsigned int percentage = (fraction > 1. ? 1. : fraction) * 100.;
	printMsg(lvlInfo, format("[%1%%%] deleting '%2%'") % percentage % path);
	printMsg(lvlInfo, std::format("[{}%] deleting '{}'", percentage, path));
    } else {
	auto freed = state.results.bytesFreed + state.bytesInvalidated;
	printMsg(lvlInfo, format("[%1%] deleting '%2%'") % showBytes(freed) % path);
	printMsg(lvlInfo, std::format("[{}] deleting '{}'", showBytes(freed), path));
    }

    state.results.paths.insert(path);


@@ 450,17 447,17 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
        // size.
        try {
            if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
                throw SysError(format("making `%1%' writable") % path);
                throw SysError(std::format("making `{}' writable", path));
            Path tmp = state.trashDir + "/" + baseNameOf(path);
            if (rename(path.c_str(), tmp.c_str()))
                throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
                throw SysError(std::format("unable to rename `{}' to `{}'", path, tmp));
            state.bytesInvalidated += size;
        } catch (SysError & e) {
            /* In a Docker container, rename(2) returns EXDEV when the source
               and destination are not both on the "top layer".  See:
               https://bugs.gnu.org/41607 */
            if (e.errNo == ENOSPC || e.errNo == EXDEV) {
                printMsg(lvlInfo, format("note: can't create move `%1%': %2%") % path % e.msg());
                printMsg(lvlInfo, std::format("note: can't create move `{}': {}", path, e.msg()));
                deleteGarbage(state, path);
            }
        }


@@ 468,7 465,7 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
        deleteGarbage(state, path);

    if (state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
        printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
        printMsg(lvlInfo, std::format("deleted or invalidated more than {} bytes; stopping", state.options.maxFreed));
        throw GCLimitReached();
    }
}


@@ 487,7 484,7 @@ bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & p
    }

    if (state.roots.find(path) != state.roots.end()) {
        printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % path);
        printMsg(lvlDebug, std::format("cannot delete `{}' because it's a root", path));
        state.alive.insert(path);
        return true;
    }


@@ 535,7 532,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)

    if (path == linksDir || path == state.trashDir) return;

    startNest(nest, lvlDebug, format("considering whether to delete `%1%'") % path);
    startNest(nest, lvlDebug, std::format("considering whether to delete `{}'", path));

    if (!isValidPath(path)) {
        /* A lock file belonging to a path that we're building right


@@ 550,7 547,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
    PathSet visited;

    if (canReachRoot(state, visited, path)) {
        printMsg(lvlDebug, format("cannot delete `%1%' because it's still reachable") % path);
        printMsg(lvlDebug, std::format("cannot delete `{}' because it's still reachable", path));
    } else {
        /* No path we visited was a root, so everything is garbage.
           But we only delete ‘path’ and its referrers here so that


@@ 571,7 568,7 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
void LocalStore::removeUnusedLinks(const GCState & state)
{
    AutoCloseDir dir = opendir(linksDir.c_str());
    if (!dir) throw SysError(format("opening directory `%1%'") % linksDir);
    if (!dir) throw SysError(std::format("opening directory `{}'", linksDir));

    long long actualSize = 0, unsharedSize = 0;



@@ 596,15 593,15 @@ void LocalStore::removeUnusedLinks(const GCState & state)
		statx_flags &= ~AT_STATX_DONT_SYNC;
		if (statx(AT_FDCWD, path.c_str(), statx_flags,
			  STATX_SIZE | STATX_NLINK, &st) == -1)
		    throw SysError(format("statting `%1%'") % path);
		    throw SysError(std::format("statting `{}'", path));
	    } else {
		throw SysError(format("statting `%1%'") % path);
		throw SysError(std::format("statting `{}'", path));
	    }
	}
#else
        struct stat st;
        if (lstat(path.c_str(), &st) == -1)
            throw SysError(format("statting `%1%'") % path);
            throw SysError(std::format("statting `{}'", path));
#endif

	/* Drop links for files smaller than 'deduplicationMinSize', even if


@@ 616,10 613,10 @@ void LocalStore::removeUnusedLinks(const GCState & state)
            continue;
        }

        printMsg(lvlTalkative, format("deleting unused link `%1%'") % path);
        printMsg(lvlTalkative, std::format("deleting unused link `{}'", path));

        if (unlink(path.c_str()) == -1)
            throw SysError(format("deleting `%1%'") % path);
            throw SysError(std::format("deleting `{}'", path));

        state.results.bytesFreed += st.st_size;
#undef st_size


@@ 628,11 625,11 @@ void LocalStore::removeUnusedLinks(const GCState & state)

    struct stat st;
    if (stat(linksDir.c_str(), &st) == -1)
        throw SysError(format("statting `%1%'") % linksDir);
        throw SysError(std::format("statting `{}'", linksDir));
    long long overhead = st.st_size;
    long long freedbytes = (unsharedSize - actualSize - overhead);

    printMsg(lvlInfo, format("note: currently hard linking saves %1%") % showBytes(freedbytes));
    printMsg(lvlInfo, std::format("note: currently hard linking saves {}", showBytes(freedbytes)));
}




@@ 662,7 659,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)

    /* Find the roots.  Since we've grabbed the GC lock, the set of
       permanent roots cannot increase now. */
    printMsg(lvlError, format("finding garbage collector roots..."));
    printMsg(lvlError, "finding garbage collector roots...");
    Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();

    for (auto& i : rootMap) state.roots.insert(i.second);


@@ 690,7 687,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
            createDirs(state.trashDir);
        } catch (SysError & e) {
            if (e.errNo == ENOSPC) {
                printMsg(lvlInfo, format("note: can't create trash directory: %1%") % e.msg());
                printMsg(lvlInfo, std::format("note: can't create trash directory: {}", e.msg()));
                state.moveToTrash = false;
            }
        }


@@ 705,20 702,20 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
            assertStorePath(i);
            tryToDelete(state, i);
            if (state.dead.find(i) == state.dead.end())
                throw Error(format("cannot delete path `%1%' since it is still alive") % i);
                throw Error(std::format("cannot delete path `{}' since it is still alive", i));
        }

    } else if (options.maxFreed > 0) {

        if (state.shouldDelete)
            printMsg(lvlError, format("deleting garbage..."));
            printMsg(lvlError, "deleting garbage...");
        else
            printMsg(lvlError, format("determining live/dead paths..."));
            printMsg(lvlError, "determining live/dead paths...");

        try {

            AutoCloseDir dir = opendir(settings.nixStore.c_str());
            if (!dir) throw SysError(format("opening directory `%1%'") % settings.nixStore);
            if (!dir) throw SysError(std::format("opening directory `{}'", settings.nixStore));

            /* Read the store and immediately delete all paths that
               aren't valid.  When using --max-freed etc., deleting


@@ 773,12 770,12 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
    fds.clear();

    /* Delete the trash directory. */
    printMsg(lvlInfo, format("deleting `%1%'") % state.trashDir);
    printMsg(lvlInfo, std::format("deleting `{}'", state.trashDir));
    deleteGarbage(state, state.trashDir);

    /* Clean up the links directory. */
    if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
        printMsg(lvlError, format("deleting unused links..."));
        printMsg(lvlError, "deleting unused links...");
        removeUnusedLinks(state);
    }


M nix/libstore/globals.cc => nix/libstore/globals.cc +4 -4
@@ 6,7 6,7 @@

#include <map>
#include <algorithm>

#include <format>

namespace nix {



@@ 156,8 156,8 @@ void Settings::_get(bool & res, const string & name)
    if (i == settings.end()) return;
    if (i->second == "true") res = true;
    else if (i->second == "false") res = false;
    else throw Error(format("configuration option `%1%' should be either `true' or `false', not `%2%'")
        % name % i->second);
    else throw Error(std::format("configuration option `{}' should be either `true' or `false', not `{}'",
        name, i->second));
}




@@ 183,7 183,7 @@ template<class N> void Settings::_get(N & res, const string & name)
    SettingsMap::iterator i = settings.find(name);
    if (i == settings.end()) return;
    if (!string2Int(i->second, res))
        throw Error(format("configuration setting `%1%' should have an integer value") % name);
        throw Error(std::format("configuration setting `{}' should have an integer value", name));
}



M nix/libstore/local-store.cc => nix/libstore/local-store.cc +81 -81
@@ 9,7 9,9 @@

#include <iostream>
#include <algorithm>
#include <format>
#include <cstring>
#include <cassert>

#include <sys/types.h>
#include <sys/stat.h>


@@ 45,12 47,12 @@ void checkStoreNotSymlink()
    struct stat st;
    while (path != "/") {
        if (lstat(path.c_str(), &st))
            throw SysError(format("getting status of `%1%'") % path);
            throw SysError(std::format("getting status of `{}'", path));
        if (S_ISLNK(st.st_mode))
            throw Error(format(
                "the path `%1%' is a symlink; "
                "this is not allowed for the store and its parent directories")
                % path);
            throw Error(std::format(
                "the path `{}' is a symlink; "
                "this is not allowed for the store and its parent directories",
                path));
        path = dirOf(path);
    }
}


@@ 86,25 88,24 @@ LocalStore::LocalStore(bool reserveSpace)
    if (getuid() == 0 && settings.buildUsersGroup != "") {

        if (chmod(perUserDir.c_str(), 0755) == -1)
            throw SysError(format("could not set permissions on '%1%' to 755")
                           % perUserDir);
            throw SysError(std::format("could not set permissions on '{}' to 755", perUserDir));

        mode_t perm = 01775;

        struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
        if (!gr)
            throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
                % settings.buildUsersGroup);
            throw Error(std::format("the group `{}' specified in `build-users-group' does not exist",
                settings.buildUsersGroup));
        else {
            struct stat st;
            if (stat(settings.nixStore.c_str(), &st))
                throw SysError(format("getting attributes of path '%1%'") % settings.nixStore);
                throw SysError(std::format("getting attributes of path '{}'", settings.nixStore));

            if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
                if (chown(settings.nixStore.c_str(), 0, gr->gr_gid) == -1)
                    throw SysError(format("changing ownership of path '%1%'") % settings.nixStore);
                    throw SysError(std::format("changing ownership of path '{}'", settings.nixStore));
                if (chmod(settings.nixStore.c_str(), perm) == -1)
                    throw SysError(format("changing permissions on path '%1%'") % settings.nixStore);
                    throw SysError(std::format("changing permissions on path '{}'", settings.nixStore));
            }
        }
    }


@@ 159,20 160,20 @@ LocalStore::LocalStore(bool reserveSpace)
       upgrade.  */
    int curSchema = getSchema();
    if (curSchema > nixSchemaVersion)
        throw Error(format("current store schema is version %1%, but I only support %2%")
            % curSchema % nixSchemaVersion);
        throw Error(std::format("current store schema is version {}, but I only support {}",
            curSchema, nixSchemaVersion));

    else if (curSchema == 0) { /* new store */
        curSchema = nixSchemaVersion;
        openDB(true);
        writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
        writeFile(schemaPath, std::format("{}", nixSchemaVersion));
    }

    else if (curSchema < nixSchemaVersion) {
	/* Guix always used version 7 of the schema.  */
	throw Error(
	    format("Your store database uses an implausibly old schema, version %1%.")
	    % curSchema);
	    std::format("Your store database uses an implausibly old schema, version {}.",
	    curSchema));
    }

    else openDB(false);


@@ 198,7 199,7 @@ int LocalStore::getSchema()
    if (pathExists(schemaPath)) {
        string s = readFile(schemaPath);
        if (!string2Int(s, curSchema))
            throw Error(format("`%1%' is corrupt") % schemaPath);
            throw Error(std::format("`{}' is corrupt", schemaPath));
    }
    return curSchema;
}


@@ 207,13 208,13 @@ int LocalStore::getSchema()
void LocalStore::openDB(bool create)
{
    if (access(settings.nixDBPath.c_str(), R_OK | W_OK))
        throw SysError(format("store database directory `%1%' is not writable") % settings.nixDBPath);
        throw SysError(std::format("store database directory `{}' is not writable", settings.nixDBPath));

    /* Open the store database. */
    string dbPath = settings.nixDBPath + "/db.sqlite";
    if (sqlite3_open_v2(dbPath.c_str(), &db.db,
            SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
        throw Error(format("cannot open store database `%1%'") % dbPath);
        throw Error(std::format("cannot open store database `{}'", dbPath));

    if (sqlite3_busy_timeout(db, 60 * 60 * 1000) != SQLITE_OK)
        throwSQLiteError(db, "setting timeout");


@@ 317,7 318,7 @@ void LocalStore::makeStoreWritable()
            throw SysError("setting up a private mount namespace");

        if (mount(0, settings.nixStore.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
            throw SysError(format("remounting %1% writable") % settings.nixStore);
            throw SysError(std::format("remounting {} writable", settings.nixStore));
    }
#endif
}


@@ 338,7 339,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct 
                 | 0444
                 | (st.st_mode & S_IXUSR ? 0111 : 0);
            if (chmod(path.c_str(), mode) == -1)
                throw SysError(format("changing mode of `%1%' to %2$o") % path % mode);
                throw SysError(std::format("changing mode of `{}' to {:o}", path, mode));
        }

    }


@@ 356,7 357,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct 
#else
        if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)
#endif
            throw SysError(format("changing modification time of `%1%'") % path);
            throw SysError(std::format("changing modification time of `{}'", path));
    }
}



@@ 365,7 366,7 @@ void canonicaliseTimestampAndPermissions(const Path & path)
{
    struct stat st;
    if (lstat(path.c_str(), &st))
        throw SysError(format("getting attributes of path `%1%'") % path);
        throw SysError(std::format("getting attributes of path `{}'", path));
    canonicaliseTimestampAndPermissions(path, st);
}



@@ 376,11 377,11 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe

    struct stat st;
    if (lstat(path.c_str(), &st))
        throw SysError(format("getting attributes of path `%1%'") % path);
        throw SysError(std::format("getting attributes of path `{}'", path));

    /* Really make sure that the path is of a supported type. */
    if (!(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)))
        throw Error(format("file ‘%1%’ has an unsupported type") % path);
        throw Error(std::format("file `{}' has an unsupported type", path));

    /* Fail if the file is not owned by the build user.  This prevents
       us from messing up the ownership/permissions of files


@@ 391,7 392,7 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
    if (fromUid != (uid_t) -1 && st.st_uid != fromUid) {
        assert(!S_ISDIR(st.st_mode));
        if (inodesSeen.find(Inode(st.st_dev, st.st_ino)) == inodesSeen.end())
            throw BuildError(format("invalid ownership on file `%1%'") % path);
            throw BuildError(std::format("invalid ownership on file `{}'", path));
        mode_t mode = st.st_mode & ~S_IFMT;
        assert(S_ISLNK(st.st_mode) || (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore));
        return;


@@ 415,8 416,8 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSe
        if (!S_ISLNK(st.st_mode) &&
            chown(path.c_str(), geteuid(), getegid()) == -1)
#endif
            throw SysError(format("changing owner of `%1%' to %2%")
                % path % geteuid());
            throw SysError(std::format("changing owner of `{}' to {}",
                path, geteuid()));
    }

    if (S_ISDIR(st.st_mode)) {


@@ 435,11 436,11 @@ void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & ino
       be a symlink, since we can't change its ownership. */
    struct stat st;
    if (lstat(path.c_str(), &st))
        throw SysError(format("getting attributes of path `%1%'") % path);
        throw SysError(std::format("getting attributes of path `{}'", path));

    if (st.st_uid != geteuid()) {
        assert(S_ISLNK(st.st_mode));
        throw Error(format("wrong ownership of top-level store path `%1%'") % path);
        throw Error(std::format("wrong ownership of top-level store path `{}'", path));
    }
}



@@ 460,7 461,7 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
    if (isFixedOutputDrv(drv)) {
        DerivationOutputs::const_iterator out = drv.outputs.find("out");
        if (out == drv.outputs.end())
            throw Error(format("derivation `%1%' does not have an output named `out'") % drvPath);
            throw Error(std::format("derivation `{}' does not have an output named `out'", drvPath));

        bool recursive; HashType ht; Hash h;
        out->second.parseHashInfo(recursive, ht, h);


@@ 468,8 469,8 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &

        StringPairs::const_iterator j = drv.env.find("out");
        if (out->second.path != outPath || j == drv.env.end() || j->second != outPath)
            throw Error(format("derivation `%1%' has incorrect output `%2%', should be `%3%'")
                % drvPath % out->second.path % outPath);
            throw Error(std::format("derivation `{}' has incorrect output `{}', should be `{}'",
                drvPath, out->second.path, outPath));
    }

    else {


@@ 485,8 486,8 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
            Path outPath = makeOutputPath(i.first, h, drvName);
            StringPairs::const_iterator j = drv.env.find(i.first);
            if (i.second.path != outPath || j == drv.env.end() || j->second != outPath)
                throw Error(format("derivation `%1%' has incorrect output `%2%', should be `%3%'")
                    % drvPath % i.second.path % outPath);
                throw Error(std::format("derivation `{}' has incorrect output `{}', should be `{}'",
                    drvPath, i.second.path, outPath));
        }
    }
}


@@ 583,12 584,12 @@ Hash parseHashField(const Path & path, const string & s)
{
    string::size_type colon = s.find(':');
    if (colon == string::npos)
        throw Error(format("corrupt hash `%1%' in valid-path entry for `%2%'")
            % s % path);
        throw Error(std::format("corrupt hash `{}' in valid-path entry for `{}'",
            s, path));
    HashType ht = parseHashType(string(s, 0, colon));
    if (ht == htUnknown)
        throw Error(format("unknown hash type `%1%' in valid-path entry for `%2%'")
            % string(s, 0, colon) % path);
        throw Error(std::format("unknown hash type `{}' in valid-path entry for `{}'",
            string(s, 0, colon), path));
    return parseHash(ht, string(s, colon + 1));
}



@@ 606,7 607,7 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
        auto useQueryPathInfo(stmtQueryPathInfo.use()(path));

        if (!useQueryPathInfo.next())
            throw Error(format("path `%1%' is not valid") % path);
            throw Error(std::format("path `{}' is not valid", path));

        info.id = useQueryPathInfo.getInt(0);



@@ 647,7 648,7 @@ uint64_t LocalStore::queryValidPathId(const Path & path)
{
    auto use(stmtQueryPathInfo.use()(path));
    if (!use.next())
        throw Error(format("path ‘%1%’ is not valid") % path);
        throw Error(std::format("path `%1%' is not valid", path));
    return use.getInt(0);
}



@@ 809,8 810,8 @@ string LocalStore::getLineFromSubstituter(Agent & run)
                if (errno == EINTR) continue;
                throw SysError("reading from substituter's stderr");
            }
            if (n == 0) throw EndOfFile(format("`%1% substitute' died unexpectedly")
					% settings.guixProgram);
            if (n == 0) throw EndOfFile(std::format("`{} substitute' died unexpectedly",
			    settings.guixProgram));
            err.append(buf, n);
            string::size_type p;
            while (((p = err.find('\n')) != string::npos)


@@ 840,7 841,7 @@ template<class T> T LocalStore::getIntLineFromSubstituter(Agent & run)
    string s = getLineFromSubstituter(run);
    T res;
    if (!string2Int(s, res))
        throw Error(format("integer expected from stream: %1%") % s);
        throw Error(std::format("integer expected from stream: {}", s));
    return res;
}



@@ 897,7 898,7 @@ void LocalStore::querySubstitutablePathInfos(PathSet & paths, SubstitutablePathI
        Path path = getLineFromSubstituter(run);
        if (path == "") break;
        if (paths.find(path) == paths.end())
            throw Error(format("got unexpected path `%1%' from substituter") % path);
            throw Error(std::format("got unexpected path `{}' from substituter", path));
        paths.erase(path);
        SubstitutablePathInfo & info(infos[path]);
        info.deriver = getLineFromSubstituter(run);


@@ 990,7 991,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
   there are no referrers. */
void LocalStore::invalidatePath(const Path & path)
{
    debug(format("invalidating path `%1%'") % path);
    debug(std::format("invalidating path `{}'", path));

    drvHashes.erase(path);



@@ 1060,7 1061,7 @@ Path LocalStore::addToStore(const string & name, const Path & _srcPath,
    bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
{
    Path srcPath(absPath(_srcPath));
    debug(format("adding `%1%' to the store") % srcPath);
    debug(std::format("adding `{}' to the store", srcPath));

    /* Read the whole path into memory. This is not a very scalable
       method for very large paths, but `copyPath' is mainly used for


@@ 1139,9 1140,9 @@ static void checkSecrecy(const Path & path)
{
    struct stat st;
    if (stat(path.c_str(), &st))
        throw SysError(format("getting status of `%1%'") % path);
        throw SysError(std::format("getting status of `{}'", path));
    if ((st.st_mode & (S_IRWXG | S_IRWXO)) != 0)
        throw Error(format("file `%1%' should be secret (inaccessible to everybody else)!") % path);
        throw Error(std::format("file `{}' should be secret (inaccessible to everybody else)!", path));
}




@@ 1212,9 1213,9 @@ static std::string signHash(const string &secretKey, const Hash &hash)
    auto hexHash = printHash(hash);

    writeLine(agent->toAgent.writeSide,
	      (format("sign %1%:%2% %3%:%4%")
	       % secretKey.size() % secretKey
	       % hexHash.size() % hexHash).str());
	    std::format("sign {}:{} {}:{}",
	        secretKey.size(), secretKey,
	        hexHash.size(), hexHash));

    return readAuthenticateReply(agent->fromAgent.readSide);
}


@@ 1226,8 1227,7 @@ static std::string verifySignature(const string &signature)
    auto agent = authenticationAgent();

    writeLine(agent->toAgent.writeSide,
	      (format("verify %1%:%2%")
	       % signature.size() % signature).str());
	    std::format("verify {}:{}", signature.size(), signature));

    return readAuthenticateReply(agent->fromAgent.readSide);
}


@@ 1237,10 1237,10 @@ void LocalStore::exportPath(const Path & path, bool sign,
{
    assertStorePath(path);

    printMsg(lvlInfo, format("exporting path `%1%'") % path);
    printMsg(lvlInfo, std::format("exporting path `{}'", path));

    if (!isValidPath(path))
        throw Error(format("path `%1%' is not valid") % path);
        throw Error(std::format("path `{}' is not valid", path));

    HashAndWriteSink hashAndWriteSink(sink);



@@ 1252,8 1252,8 @@ void LocalStore::exportPath(const Path & path, bool sign,
    Hash hash = hashAndWriteSink.currentHash();
    Hash storedHash = queryPathHash(path);
    if (hash != storedHash && storedHash != Hash(storedHash.type))
        throw Error(format("hash of path `%1%' has changed from `%2%' to `%3%'!") % path
            % printHash(storedHash) % printHash(hash));
        throw Error(std::format("hash of path `{}' has changed from `{}' to `{}'!",
            path, printHash(storedHash), printHash(hash)));

    writeInt(EXPORT_MAGIC, hashAndWriteSink);



@@ 1347,7 1347,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
    bool haveSignature = readInt(hashAndReadSource) == 1;

    if (requireSignature && !haveSignature)
        throw Error(format("imported archive of `%1%' lacks a signature") % dstPath);
        throw Error(std::format("imported archive of `{}' lacks a signature", dstPath));

    if (haveSignature) {
        string signature = readString(hashAndReadSource);


@@ 1387,8 1387,8 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
            if (pathExists(dstPath)) deletePath(dstPath);

            if (rename(unpacked.c_str(), dstPath.c_str()) == -1)
                throw SysError(format("cannot move `%1%' to `%2%'")
                    % unpacked % dstPath);
                throw SysError(std::format("cannot move `{}' to `{}'",
                    unpacked, dstPath));

            canonicalisePathMetaData(dstPath, -1);



@@ 1438,8 1438,8 @@ void LocalStore::invalidatePathChecked(const Path & path)
            PathSet referrers; queryReferrers_(path, referrers);
            referrers.erase(path); /* ignore self-references */
            if (!referrers.empty())
                throw PathInUse(format("cannot delete path `%1%' because it is in use by %2%")
                    % path % showPaths(referrers));
                throw PathInUse(std::format("cannot delete path `{}' because it is in use by {}",
                    path, showPaths(referrers)));
            invalidatePath(path);
        }



@@ 1450,7 1450,7 @@ void LocalStore::invalidatePathChecked(const Path & path)

bool LocalStore::verifyStore(bool checkContents, bool repair)
{
    printMsg(lvlError, format("reading the store..."));
    printMsg(lvlError, "reading the store...");

    bool errors = false;



@@ 1483,13 1483,13 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
                ValidPathInfo info = queryPathInfo(i);

                /* Check the content hash (optionally - slow). */
                printMsg(lvlTalkative, format("checking contents of `%1%'") % i);
                printMsg(lvlTalkative, std::format("checking contents of `{}'", i));
                HashResult current = hashPath(info.hash.type, i);

                if (info.hash != nullHash && info.hash != current.first) {
                    printMsg(lvlError, format("path `%1%' was modified! "
                            "expected hash `%2%', got `%3%'")
                        % i % printHash(info.hash) % printHash(current.first));
                    printMsg(lvlError, std::format("path `{}' was modified! "
                            "expected hash `{}', got `{}'",
                        i, printHash(info.hash), printHash(current.first)));
                    if (repair) repairPath(i); else errors = true;
                } else {



@@ 1497,14 1497,14 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)

                    /* Fill in missing hashes. */
                    if (info.hash == nullHash) {
                        printMsg(lvlError, format("fixing missing hash on `%1%'") % i);
                        printMsg(lvlError, std::format("fixing missing hash on `{}'", i));
                        info.hash = current.first;
                        update = true;
                    }

                    /* Fill in missing narSize fields (from old stores). */
                    if (info.narSize == 0) {
                        printMsg(lvlError, format("updating size field on `%1%' to %2%") % i % current.second);
                        printMsg(lvlError, std::format("updating size field on `{}' to {}", i, current.second));
                        info.narSize = current.second;
                        update = true;
                    }


@@ 1517,9 1517,9 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
                /* It's possible that the path got GC'ed, so ignore
                   errors on invalid paths. */
                if (isValidPath(i))
                    printMsg(lvlError, format("error: %1%") % e.msg());
                    printMsg(lvlError, std::format("error: {}", e.msg()));
                else
                    printMsg(lvlError, format("warning: %1%") % e.msg());
                    printMsg(lvlError, std::format("warning: {}", e.msg()));
                errors = true;
            }
        }


@@ 1538,7 1538,7 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
    done.insert(path);

    if (!isStorePath(path)) {
        printMsg(lvlError, format("path `%1%' is not in the store") % path);
        printMsg(lvlError, std::format("path `{}' is not in the store", path));
        invalidatePath(path);
        return;
    }


@@ 1556,15 1556,15 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
            }

        if (canInvalidate) {
            printMsg(lvlError, format("path `%1%' disappeared, removing from database...") % path);
            printMsg(lvlError, std::format("path `{}' disappeared, removing from database...", path));
            invalidatePath(path);
        } else {
            printMsg(lvlError, format("path `%1%' disappeared, but it still has valid referrers!") % path);
            printMsg(lvlError, std::format("path `{}' disappeared, but it still has valid referrers!", path));
            if (repair)
                try {
                    repairPath(path);
                } catch (Error & e) {
                    printMsg(lvlError, format("warning: %1%") % e.msg());
                    printMsg(lvlError, std::format("warning: {}", e.msg()));
                    errors = true;
                }
            else errors = true;


@@ 1581,7 1581,7 @@ bool LocalStore::pathContentsGood(const Path & path)
{
    std::map<Path, bool>::iterator i = pathContentsGoodCache.find(path);
    if (i != pathContentsGoodCache.end()) return i->second;
    printMsg(lvlInfo, format("checking path `%1%'...") % path);
    printMsg(lvlInfo, std::format("checking path `{}'...", path));
    ValidPathInfo info = queryPathInfo(path);
    bool res;
    if (!pathExists(path))


@@ 1592,7 1592,7 @@ bool LocalStore::pathContentsGood(const Path & path)
        res = info.hash == nullHash || info.hash == current.first;
    }
    pathContentsGoodCache[path] = res;
    if (!res) printMsg(lvlError, format("path `%1%' is corrupted or missing!") % path);
    if (!res) printMsg(lvlError, std::format("path `{}' is corrupted or missing!", path));
    return res;
}



@@ 1617,14 1617,14 @@ void LocalStore::createUser(const std::string & userName, uid_t userId)
    auto created = createDirs(dir);
    if (!created.empty()) {
	if (chmod(dir.c_str(), 0755) == -1)
	    throw SysError(format("changing permissions of directory '%s'") % dir);
	    throw SysError(std::format("changing permissions of directory '{}'", dir));

	/* The following operation requires CAP_CHOWN or can be handled
	   manually by a user with CAP_CHOWN.  */
	if (chown(dir.c_str(), userId, -1) == -1) {
	    rmdir(dir.c_str());
	    string message = strerror(errno);
	    printMsg(lvlInfo, format("failed to change owner of directory '%1%' to %2%: %3%") % dir % userId % message);
	    printMsg(lvlInfo, std::format("failed to change owner of directory '{}' to {}: {}", dir, userId, message));
	}
    }
}

M nix/libstore/misc.cc => nix/libstore/misc.cc +9 -8
@@ 4,6 4,7 @@
#include "local-store.hh"
#include "globals.hh"

#include <format>

namespace nix {



@@ 65,7 66,7 @@ static void dfsVisit(StoreAPI & store, const PathSet & paths,
    PathSet & parents)
{
    if (parents.find(path) != parents.end())
        throw BuildError(format("cycle detected in the references of `%1%'") % path);
        throw BuildError(std::format("cycle detected in the references of `{}'", path));

    if (visited.find(path) != visited.end()) return;
    visited.insert(path);


@@ 99,19 100,19 @@ Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
string showBytes(long long bytes)
{
    if (llabs(bytes > exp2l(60))) {
        return (format("%7.2f EiB") % (bytes / exp2l(60))).str();
        return std::format("{:7.2f} EiB", bytes / exp2l(60));
    } else if (llabs(bytes > exp2l(50))) {
        return (format("%7.2f PiB") % (bytes / exp2l(50))).str();
        return std::format("{:7.2f} PiB", bytes / exp2l(50));
    } else if (llabs(bytes > exp2l(40))) {
        return (format("%7.2f TiB") % (bytes / exp2l(40))).str();
        return std::format("{:7.2f} TiB", bytes / exp2l(40));
    } else if (llabs(bytes > exp2l(30))) {
        return (format("%7.2f GiB") % (bytes / exp2l(30))).str();
        return std::format("{:7.2f} GiB", bytes / exp2l(30));
    } else if (llabs(bytes > exp2l(20))) {
        return (format("%7.2f MiB") % (bytes / exp2l(20))).str();
        return std::format("{:7.2f} MiB", bytes / exp2l(20));
    } else if (llabs(bytes > exp2l(10))) {
        return (format("%7.2f KiB") % (bytes / exp2l(10))).str();
        return std::format("{:7.2f} KiB", bytes / exp2l(10));
    } else {
        return (format("%4f  bytes") % bytes).str();
        return std::format("{:4}  bytes", bytes);
    }
}


M nix/libstore/optimise-store.cc => nix/libstore/optimise-store.cc +30 -31
@@ 12,7 12,7 @@
#include <unistd.h>
#include <errno.h>
#include <stdio.h>

#include <format>

namespace nix {



@@ 24,9 24,9 @@ static void makeWritable(const Path & path)
{
    struct stat st;
    if (lstat(path.c_str(), &st))
        throw SysError(format("getting attributes of path `%1%'") % path);
        throw SysError(std::format("getting attributes of path `{}'", path));
    if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
        throw SysError(format("changing writability of `%1%'") % path);
        throw SysError(std::format("changing writability of `{}'", path));
}




@@ 52,7 52,7 @@ LocalStore::InodeHash LocalStore::loadInodeHash()
    InodeHash inodeHash;

    AutoCloseDir dir = opendir(linksDir.c_str());
    if (!dir) throw SysError(format("opening directory `%1%'") % linksDir);
    if (!dir) throw SysError(std::format("opening directory `{}'", linksDir));

    struct dirent * dirent;
    while (errno = 0, dirent = readdir(dir)) { /* sic */


@@ 60,9 60,9 @@ LocalStore::InodeHash LocalStore::loadInodeHash()
        // We don't care if we hit non-hash files, anything goes
        inodeHash.insert(dirent->d_ino);
    }
    if (errno) throw SysError(format("reading directory `%1%'") % linksDir);
    if (errno) throw SysError(std::format("reading directory `{}'", linksDir));

    printMsg(lvlTalkative, format("loaded %1% hash inodes") % inodeHash.size());
    printMsg(lvlTalkative, std::format("loaded {} hash inodes", inodeHash.size()));

    return inodeHash;
}


@@ 73,14 73,14 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
    Strings names;

    AutoCloseDir dir = opendir(path.c_str());
    if (!dir) throw SysError(format("opening directory `%1%'") % path);
    if (!dir) throw SysError(std::format("opening directory `{}'", path));

    struct dirent * dirent;
    while (errno = 0, dirent = readdir(dir)) { /* sic */
        checkInterrupt();

        if (inodeHash.count(dirent->d_ino)) {
            printMsg(lvlDebug, format("`%1%' is already linked") % dirent->d_name);
            printMsg(lvlDebug, std::format("`{}' is already linked", dirent->d_name));
            continue;
        }



@@ 88,7 88,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
        if (name == "." || name == "..") continue;
        names.push_back(name);
    }
    if (errno) throw SysError(format("reading directory `%1%'") % path);
    if (errno) throw SysError(std::format("reading directory `{}'", path));

    return names;
}


@@ 100,7 100,7 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa

    struct stat st;
    if (lstat(path.c_str(), &st))
        throw SysError(format("getting attributes of path `%1%'") % path);
        throw SysError(std::format("getting attributes of path `{}'", path));

    if (S_ISDIR(st.st_mode)) {
        Strings names = readDirectoryIgnoringInodes(path, inodeHash);


@@ 121,13 121,13 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
       Guix System (example: $fontconfig/var/cache being modified).  Skip
       those files.  FIXME: check the modification time. */
    if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) {
        printMsg(lvlError, format("skipping suspicious writable file `%1%'") % path);
        printMsg(lvlError, std::format("skipping suspicious writable file `{}'", path));
        return;
    }

    /* This can still happen on top-level files. */
    if (st.st_nlink > 1 && inodeHash.count(st.st_ino)) {
        printMsg(lvlDebug, format("`%1%' is already linked, with %2% other file(s).") % path % (st.st_nlink - 2));
        printMsg(lvlDebug, std::format("`{}' is already linked, with {} other file(s).", path, (st.st_nlink - 2)));
        return;
    }



@@ 141,7 141,7 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
       contents of the symlink (i.e. the result of readlink()), not
       the contents of the target (which may not even exist). */
    Hash hash = hashPath(htSHA256, path).first;
    printMsg(lvlDebug, format("`%1%' has hash `%2%'") % path % printHash(hash));
    printMsg(lvlDebug, std::format("`{}' has hash `{}'", path, printHash(hash)));

    /* Check if this is a known hash. */
    Path linkPath = linksDir + "/" + printHash32(hash);


@@ 164,12 164,12 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
	    /* On ext4, that probably means the directory index is full.  When
	       that happens, it's fine to ignore it: we just effectively
	       disable deduplication of this file.  */
	    printMsg(lvlInfo, format("cannot link `%1%' to `%2%': %3%")
		     % linkPath % path % strerror(ENOSPC));
	    printMsg(lvlInfo, std::format("cannot link `{}' to `{}': {}",
		    linkPath, path, strerror(ENOSPC)));
	    return;

	default:
            throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path);
            throw SysError(std::format("cannot link `{}' to `{}'", linkPath, path));
	}
    }



@@ 177,20 177,20 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
       current file with a hard link to that file. */
    struct stat stLink;
    if (lstat(linkPath.c_str(), &stLink))
        throw SysError(format("getting attributes of path `%1%'") % linkPath);
        throw SysError(std::format("getting attributes of path `{}'", linkPath));

    if (st.st_ino == stLink.st_ino) {
        printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % linkPath);
        printMsg(lvlDebug, std::format("`{}' is already linked to `{}'", path, linkPath));
        return;
    }

    if (st.st_size != stLink.st_size) {
        printMsg(lvlError, format("removing corrupted link ‘%1%’") % linkPath);
        printMsg(lvlError, std::format("removing corrupted link `%1%'", linkPath));
        unlink(linkPath.c_str());
        goto retry;
    }

    printMsg(lvlTalkative, format("linking ‘%1%’ to ‘%2%’") % path % linkPath);
    printMsg(lvlTalkative, std::format("linking `%1%' to `%2%'", path, linkPath));

    /* Make the containing directory writable, but only if it's not
       the store itself (we don't want or need to mess with its


@@ 202,8 202,7 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
       its timestamp back to 0. */
    MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");

    Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
        % settings.nixStore % getpid() % rand()).str();
    Path tempLink = std::format("{}/.tmp-link-{}-{}", settings.nixStore, getpid(), rand());

    if (link(linkPath.c_str(), tempLink.c_str()) == -1) {
        if (errno == EMLINK) {


@@ 211,27 210,27 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
               systems).  This is likely to happen with empty files.
               Just shrug and ignore. */
            if (st.st_size)
                printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath);
                printMsg(lvlInfo, std::format("`{}' has maximum number of links", linkPath));
            return;
        }
	    throw SysError(format("cannot link `%1%' to `%2%'") % tempLink % linkPath);
	    throw SysError(std::format("cannot link `{}' to `{}'", tempLink, linkPath));
	}

    /* Atomically replace the old file with the new hard link. */
    if (rename(tempLink.c_str(), path.c_str()) == -1) {
	int renameErrno = errno;
        if (unlink(tempLink.c_str()) == -1)
            printMsg(lvlError, format("unable to unlink `%1%'") % tempLink);
            printMsg(lvlError, std::format("unable to unlink `{}'", tempLink));
        if (renameErrno == EMLINK) {
            /* Some filesystems generate too many links on the rename,
               rather than on the original link.  (Probably it
               temporarily increases the st_nlink field before
               decreasing it again.) */
            if (st.st_size)
                printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath);
                printMsg(lvlInfo, std::format("`{}' has maximum number of links", linkPath));
            return;
        }
        throw SysError(format("cannot rename `%1%' to `%2%'") % tempLink % path);
        throw SysError(std::format("cannot rename `{}' to `{}'", tempLink, path));
    }

    stats.filesLinked++;


@@ 248,7 247,7 @@ void LocalStore::optimiseStore(OptimiseStats & stats)
    for (auto& i : paths) {
        addTempRoot(i);
        if (!isValidPath(i)) continue; /* path was GC'ed, probably */
        startNest(nest, lvlChatty, format("hashing files in `%1%'") % i);
        startNest(nest, lvlChatty, std::format("hashing files in `{}'", i));
        optimisePath_(stats, i, inodeHash);
    }
}


@@ 260,9 259,9 @@ void LocalStore::optimiseStore()
    optimiseStore(stats);

    printMsg(lvlError,
        format("%1% freed by hard-linking %2% files")
        % showBytes(stats.bytesFreed)
        % stats.filesLinked);
        std::format("{} freed by hard-linking {} files",
        showBytes(stats.bytesFreed),
        stats.filesLinked));
}

void LocalStore::optimisePath(const Path & path)

M nix/libstore/pathlocks.cc => nix/libstore/pathlocks.cc +11 -9
@@ 3,6 3,8 @@

#include <cerrno>
#include <cstdlib>
#include <cassert>
#include <format>

#include <sys/types.h>
#include <sys/stat.h>


@@ 18,7 20,7 @@ int openLockFile(const Path & path, bool create)

    fd = open(path.c_str(), O_RDWR | (create ? O_CREAT : 0), 0600);
    if (fd == -1 && (create || errno != ENOENT))
        throw SysError(format("opening lock file `%1%'") % path);
        throw SysError(std::format("opening lock file `{}'", path));

    closeOnExec(fd);



@@ 54,14 56,14 @@ bool lockFile(int fd, LockType lockType, bool wait)
        while (fcntl(fd, F_SETLKW, &lock) != 0) {
            checkInterrupt();
            if (errno != EINTR)
                throw SysError(format("acquiring/releasing lock"));
                throw SysError("acquiring/releasing lock");
        }
    } else {
        while (fcntl(fd, F_SETLK, &lock) != 0) {
            checkInterrupt();
            if (errno == EACCES || errno == EAGAIN) return false;
            if (errno != EINTR)
                throw SysError(format("acquiring/releasing lock"));
                throw SysError("acquiring/releasing lock");
        }
    }



@@ 109,7 111,7 @@ bool PathLocks::lockPaths(const PathSet & _paths,
        Path path = i;
        Path lockPath = path + ".lock";

        debug(format("locking path `%1%'") % path);
        debug(std::format("locking path `{}'", path));

        if (lockedPaths.find(lockPath) != lockedPaths.end())
            throw Error("deadlock: trying to re-acquire self-held lock");


@@ 134,19 136,19 @@ bool PathLocks::lockPaths(const PathSet & _paths,
                }
            }

            debug(format("lock acquired on `%1%'") % lockPath);
            debug(std::format("lock acquired on `{}'", lockPath));

            /* Check that the lock file hasn't become stale (i.e.,
               hasn't been unlinked). */
            struct stat st;
            if (fstat(fd, &st) == -1)
                throw SysError(format("statting lock file `%1%'") % lockPath);
                throw SysError(std::format("statting lock file `{}'", lockPath));
            if (st.st_size != 0)
                /* This lock file has been unlinked, so we're holding
                   a lock on a deleted file.  This means that other
                   processes may create and acquire a lock on
                   `lockPath', and proceed.  So we must retry. */
                debug(format("open lock file `%1%' has become stale") % lockPath);
                debug(std::format("open lock file `{}' has become stale", lockPath));
            else
                break;
        }


@@ 178,9 180,9 @@ void PathLocks::unlock()
        lockedPaths.erase(i.second);
        if (close(i.first) == -1)
            printMsg(lvlError,
                format("error (ignored): cannot close lock file on `%1%'") % i.second);
                std::format("error (ignored): cannot close lock file on `{}'", i.second));

        debug(format("lock released on `%1%'") % i.second);
        debug(std::format("lock released on `{}'", i.second));
    }

    fds.clear();

M nix/libstore/references.cc => nix/libstore/references.cc +4 -4
@@ 5,7 5,8 @@

#include <map>
#include <cstdlib>

#include <cassert>
#include <format>

namespace nix {



@@ 37,8 38,7 @@ static void search(const unsigned char * s, unsigned int len,
        if (!match) continue;
        string ref((const char *) s + i, refLength);
        if (hashes.find(ref) != hashes.end()) {
            debug(format("found reference to `%1%' at offset `%2%'")
                  % ref % i);
            debug(std::format("found reference to `{}' at offset `{}'", ref, i));
            seen.insert(ref);
            hashes.erase(ref);
        }


@@ 93,7 93,7 @@ PathSet scanForReferences(const string & path,
        string baseName = baseNameOf(i);
        string::size_type pos = baseName.find('-');
        if (pos == string::npos)
            throw Error(format("bad reference `%1%'") % i);
            throw Error(std::format("bad reference `{}'", i));
        string s = string(baseName, 0, pos);
        assert(s.size() == refLength);
        assert(backMap.find(s) == backMap.end());

M nix/libstore/sqlite.cc => nix/libstore/sqlite.cc +6 -3
@@ 1,11 1,14 @@
#include "sqlite.hh"
#include "util.hh"

#include <format>
#include <cassert>

#include <sqlite3.h>

namespace nix {

[[noreturn]] void throwSQLiteError(sqlite3 * db, const format & f)
[[noreturn]] void throwSQLiteError(sqlite3 * db, std::string_view f)
{
    int err = sqlite3_errcode(db);
    if (err == SQLITE_BUSY || err == SQLITE_PROTOCOL) {


@@ 28,10 31,10 @@ namespace nix {
#else
        sleep(1);
#endif
        throw SQLiteBusy(format("%1%: %2%") % f.str() % sqlite3_errmsg(db));
        throw SQLiteBusy(std::format("{}: {}", f, sqlite3_errmsg(db)));
    }
    else
        throw SQLiteError(format("%1%: %2%") % f.str() % sqlite3_errmsg(db));
        throw SQLiteError(std::format("{}: {}", f, sqlite3_errmsg(db)));
}

SQLite::~SQLite()

M nix/libstore/sqlite.hh => nix/libstore/sqlite.hh +2 -1
@@ 3,6 3,7 @@
#include <functional>
#include <string>
#include <cstdint>
#include <string_view>

#include "types.hh"



@@ 85,7 86,7 @@ struct SQLiteTxn
MakeError(SQLiteError, Error);
MakeError(SQLiteBusy, SQLiteError);

[[noreturn]] void throwSQLiteError(sqlite3 * db, const format & f);
[[noreturn]] void throwSQLiteError(sqlite3 * db, std::string_view f);

/* Convenience function for retrying a SQLite transaction when the
   database is busy. */

M nix/libstore/store-api.cc => nix/libstore/store-api.cc +7 -8
@@ 3,7 3,7 @@
#include "util.hh"

#include <climits>

#include <format>

namespace nix {



@@ 32,14 32,14 @@ bool isStorePath(const Path & path)
void assertStorePath(const Path & path)
{
    if (!isStorePath(path))
        throw Error(format("path `%1%' is not in the store") % path);
        throw Error(std::format("path `{}' is not in the store", path));
}


Path toStorePath(const Path & path)
{
    if (!isInStore(path))
        throw Error(format("path `%1%' is not in the store") % path);
        throw Error(std::format("path `{}' is not in the store", path));
    Path::size_type slash = path.find('/', settings.nixStore.size() + 1);
    if (slash == Path::npos)
        return path;


@@ 61,15 61,14 @@ void checkStoreName(const string & name)
    /* Disallow names starting with a dot for possible security
       reasons (e.g., "." and ".."). */
    if (string(name, 0, 1) == ".")
        throw Error(format("invalid name: `%1%' (can't begin with dot)") % name);
        throw Error(std::format("invalid name: `{}' (can't begin with dot)", name));
    for (const auto& i : name)
        if (!((i >= 'A' && i <= 'Z') ||
              (i >= 'a' && i <= 'z') ||
              (i >= '0' && i <= '9') ||
              validChars.find(i) != string::npos))
        {
            throw Error(format("invalid character `%1%' in name `%2%'")
                % i % name);
            throw Error(std::format("invalid character `{}' in name `{}'", i, name));
        }
}



@@ 211,13 210,13 @@ string StoreAPI::makeValidityRegistration(const PathSet & paths,

        if (showHash) {
            s += printHash(info.hash) + "\n";
            s += (format("%1%\n") % info.narSize).str();
            s += std::format("{}\n", info.narSize);
        }

        Path deriver = showDerivers ? info.deriver : "";
        s += deriver + "\n";

        s += (format("%1%\n") % info.references.size()).str();
        s += std::format("{}\n", info.references.size());

        for (auto& j : info.references)
            s += j + "\n";

M nix/libutil/affinity.cc => nix/libutil/affinity.cc +4 -2
@@ 2,6 2,8 @@
#include "util.hh"
#include "affinity.hh"

#include <format>
 
#if HAVE_SCHED_H
#include <sched.h>
#endif


@@ 20,12 22,12 @@ void setAffinityTo(int cpu)
#if HAVE_SCHED_SETAFFINITY
    if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return;
    didSaveAffinity = true;
    printMsg(lvlDebug, format("locking this thread to CPU %1%") % cpu);
    printMsg(lvlDebug, std::format("locking this thread to CPU {}", cpu));
    cpu_set_t newAffinity;
    CPU_ZERO(&newAffinity);
    CPU_SET(cpu, &newAffinity);
    if (sched_setaffinity(0, sizeof(cpu_set_t), &newAffinity) == -1)
        printMsg(lvlError, format("failed to lock thread to CPU %1%") % cpu);
        printMsg(lvlError, std::format("failed to lock thread to CPU {}", cpu));
#endif
}


M nix/libutil/archive.cc => nix/libutil/archive.cc +8 -7
@@ 6,6 6,7 @@
#include <algorithm>
#include <vector>
#include <map>
#include <format>

#include <strings.h> // for strcasecmp



@@ 35,7 36,7 @@ static void dumpContents(const Path & path, size_t size,
    writeLongLong(size, sink);

    AutoCloseFD fd = open(path.c_str(), O_RDONLY);
    if (fd == -1) throw SysError(format("opening file `%1%'") % path);
    if (fd == -1) throw SysError(std::format("opening file `{}'", path));

    unsigned char buf[65536];
    size_t left = size;


@@ 55,7 56,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
{
    struct stat st;
    if (lstat(path.c_str(), &st))
        throw SysError(format("getting attributes of path `%1%'") % path);
        throw SysError(std::format("getting attributes of path `{}'", path));

    writeString("(", sink);



@@ 98,7 99,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
        writeString(readLink(path), sink);
    }

    else throw Error(format("file `%1%' has an unsupported type") % path);
    else throw Error(std::format("file `{}' has an unsupported type", path));

    writeString(")", sink);
}


@@ 227,7 228,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
                } else if (s == "name") {
                    name = readString(source);
                    if (name.empty() || name == "." || name == ".." || name.find('/') != string::npos || name.find((char) 0) != string::npos)
                        throw Error(format("NAR contains invalid file name `%1%'") % name);
                        throw Error(std::format("NAR contains invalid file name `{}'", name));
                    if (name <= prevName)
                        throw Error("NAR directory is not sorted");
                    prevName = name;


@@ 274,7 275,7 @@ struct RestoreSink : ParseSink
    {
        Path p = dstPath + path;
        if (mkdir(p.c_str(), 0777) == -1)
            throw SysError(format("creating directory `%1%'") % p);
            throw SysError(std::format("creating directory `{}'", p));
    };

    void createRegularFile(const Path & path)


@@ 282,7 283,7 @@ struct RestoreSink : ParseSink
        Path p = dstPath + path;
        fd.close();
        fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
        if (fd == -1) throw SysError(format("creating file `%1%'") % p);
        if (fd == -1) throw SysError(std::format("creating file `{}'", p));
    }

    void isExecutable()


@@ 304,7 305,7 @@ struct RestoreSink : ParseSink
               OpenSolaris).  Since preallocation is just an
               optimisation, ignore it. */
            if (errno && errno != EINVAL)
                throw SysError(format("preallocating file of %1% bytes") % len);
                throw SysError(std::format("preallocating file of {} bytes", len));
        }
#endif
    }

M nix/libutil/hash.cc => nix/libutil/hash.cc +15 -12
@@ 2,6 2,9 @@

#include <iostream>
#include <cstring>
#include <cassert>
#include <string_view>
#include <format>

#include "hash.hh"
#include "archive.hh"


@@ 73,18 76,18 @@ string printHash(const Hash & hash)
}


Hash parseHash(HashType ht, const string & s)
Hash parseHash(HashType ht, std::string_view s)
{
    Hash hash(ht);
    if (s.length() != hash.hashSize * 2) {
	string algo = gcry_md_algo_name(ht);
        throw Error(format("invalid %1% hash '%2%' (%3% bytes but expected %4%)")
		    % algo % s % (s.length() / 2) % hash.hashSize);
        throw Error(std::format("invalid {} hash '{}' ({} bytes but expected {})",
		    algo, s, (s.length() / 2), hash.hashSize));
    }
    for (unsigned int i = 0; i < hash.hashSize; i++) {
        string s2(s, i * 2, 2);
        if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
            throw Error(format("invalid hash `%1%'") % s);
            throw Error(std::format("invalid hash `{}'", s));
        std::istringstream str(s2);
        int n;
        str >> std::hex >> n;


@@ 132,7 135,7 @@ string printHash16or32(const Hash & hash)
}


Hash parseHash32(HashType ht, const string & s)
Hash parseHash32(HashType ht, std::string_view s)
{
    Hash hash(ht);
    unsigned int len = hashLength32(ht);


@@ 144,7 147,7 @@ Hash parseHash32(HashType ht, const string & s)
        for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
            if (base32Chars[digit] == c) break;
        if (digit >= 32)
            throw Error(format("invalid base-32 hash '%1%'") % s);
            throw Error(std::format("invalid base-32 hash '{}'", s));
        unsigned int b = n * 5;
        unsigned int i = b / 8;
        unsigned int j = b % 8;


@@ 156,7 159,7 @@ Hash parseHash32(HashType ht, const string & s)
}


Hash parseHash16or32(HashType ht, const string & s)
Hash parseHash16or32(HashType ht, std::string_view s)
{
    Hash hash(ht);
    if (s.size() == hash.hashSize * 2)


@@ 166,13 169,13 @@ Hash parseHash16or32(HashType ht, const string & s)
        /* base-32 representation */
        hash = parseHash32(ht, s);
    else
        throw Error(format("hash `%1%' has wrong length for hash type `%2%'")
            % s % printHashType(ht));
        throw Error(std::format("hash `{}' has wrong length for hash type `{}'",
            s, printHashType(ht)));
    return hash;
}


bool isHash(const string & s)
bool isHash(std::string_view s)
{
    if (s.length() != 32) return false;
    for (int i = 0; i < 32; i++) {


@@ 247,13 250,13 @@ Hash hashFile(HashType ht, const Path & path)
    start(ht, ctx);

    AutoCloseFD fd = open(path.c_str(), O_RDONLY);
    if (fd == -1) throw SysError(format("computing hash of file `%1%'") % path);
    if (fd == -1) throw SysError(std::format("computing hash of file `{}'", path));

    unsigned char buf[8192];
    ssize_t n;
    while ((n = read(fd, buf, sizeof(buf)))) {
        checkInterrupt();
        if (n == -1) throw SysError(format("reading file `%1%'") % path);
        if (n == -1) throw SysError(std::format("reading file `{}'", path));
        update(ht, ctx, buf, n);
    }


M nix/libutil/hash.hh => nix/libutil/hash.hh +4 -4
@@ 51,7 51,7 @@ struct Hash
string printHash(const Hash & hash);

/* Parse a hexadecimal representation of a hash code. */
Hash parseHash(HashType ht, const string & s);
Hash parseHash(HashType ht, std::string_view s);

/* Returns the length of a base-32 hash representation. */
unsigned int hashLength32(const Hash & hash);


@@ 63,13 63,13 @@ string printHash32(const Hash & hash);
string printHash16or32(const Hash & hash);

/* Parse a base-32 representation of a hash code. */
Hash parseHash32(HashType ht, const string & s);
Hash parseHash32(HashType ht, std::string_view s);

/* Parse a base-16 or base-32 representation of a hash code. */
Hash parseHash16or32(HashType ht, const string & s);
Hash parseHash16or32(HashType ht, std::string_view s);

/* Verify that the given string is a valid hash code. */
bool isHash(const string & s);
bool isHash(std::string_view s);

/* Compute the hash of the given string. */
Hash hashString(HashType ht, const string & s);

M nix/libutil/serialise.cc => nix/libutil/serialise.cc +1 -1
@@ 3,7 3,7 @@

#include <cstring>
#include <cerrno>

#include <cassert>

namespace nix {


M nix/libutil/spawn.cc => nix/libutil/spawn.cc +32 -27
@@ 30,6 30,8 @@
#include <cstring>
#include <cstdlib>
#include <cstdint>
#include <cassert>
#include <format>

#if HAVE_SYS_MOUNT_H
#include <sys/mount.h>


@@ 61,8 63,11 @@
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root,put_old))
#endif


#define CLONE_ENABLED defined(CLONE_NEWNS)
#if defined(CLONE_NEWNS)
#define CLONE_ENABLED 1
#else
#define CLONE_ENABLED 0
#endif

#if CLONE_ENABLED
#include <sys/ioctl.h>


@@ 84,7 89,7 @@ void addPhaseAfter(Phases & phases, string afterLabel, string addLabel, Action a
            phases.insert(i, p);
            return;
        }
    throw Error(format("label `%1%' not found in phases") % afterLabel);
    throw Error(std::format("label `{}' not found in phases", afterLabel));
}




@@ 98,7 103,7 @@ void addPhaseBefore(Phases & phases, string beforeLabel, string addLabel, Action
            phases.insert(i, p);
            return;
        }
    throw Error(format("label `%1%' not found in phases") % beforeLabel);
    throw Error(std::format("label `{}' not found in phases", beforeLabel));
}




@@ 127,7 132,7 @@ void deletePhase(Phases & phases, string delLabel)
            phases.erase(i);
            return;
        }
    throw Error(format("label `%1%' not found in phases") % delLabel);
    throw Error(std::format("label `{}' not found in phases", delLabel));
}




@@ 138,7 143,7 @@ void replacePhase(Phases & phases, string replaceLabel, Action newAction)
            (*i).action = newAction;
            return;
        }
    throw Error(format("label `%1' not found in phases") % replaceLabel);
    throw Error(std::format("label `{}' not found in phases", replaceLabel));
}




@@ 190,7 195,7 @@ static void earlyIOSetupAction(SpawnContext & ctx)
            /* Doesn't make sense for it to be writable, but compatibility... */
            AutoCloseFD fd = open(ctx.stdinFile.c_str(), O_RDWR);
            if(fd == -1)
                throw SysError(format("cannot open `%1%'") % ctx.stdinFile);
                throw SysError(std::format("cannot open `{}'", ctx.stdinFile));
            if(dup2(fd, STDIN_FILENO) == -1)
                throw SysError("cannot dup2 fd into stdin fd");
        }


@@ 216,7 221,7 @@ static void chrootAction(SpawnContext & ctx)
    if(ctx.doChroot)
#if HAVE_CHROOT
        if(chroot(ctx.chrootRootDir.c_str()) == -1)
            throw SysError(format("cannot change root directory to '%1%'") % ctx.chrootRootDir);
            throw SysError(std::format("cannot change root directory to '{}'", ctx.chrootRootDir));
#else
    throw Error("chroot is not supported on this system");
#endif


@@ 227,7 232,7 @@ static void chdirAction(SpawnContext & ctx)
{
    if(ctx.setcwd)
        if(chdir(ctx.cwd.c_str()) == -1)
            throw SysError(format("changing into `%1%'") % ctx.cwd);
            throw SysError(std::format("changing into `{}'", ctx.cwd));
}




@@ 353,7 358,7 @@ void execAction(SpawnContext & ctx)	  // kept public for use in 'build.cc'
        env = envPtrs.data();
    }
    if(execvpe(ctx.program.c_str(), stringsToCharPtrs(ctx.args).data(), env) == -1)
        throw SysError(format("executing `%1%'") % ctx.program);
        throw SysError(std::format("executing `{}'", ctx.program));
}




@@ 478,11 483,11 @@ static void makeChrootSeparateFilesystemAction(SpawnContext & sctx)
           a tmpfs on it. */
        if(ctx.mountTmpfsOnChroot) {
            if(mount("none", ctx.chrootRootDir.c_str(), "tmpfs", 0, 0) == -1)
                throw SysError(format("unable to mount tmpfs on `%1%'") % ctx.chrootRootDir);
                throw SysError(std::format("unable to mount tmpfs on `{}'", ctx.chrootRootDir));
        }
        else {
            if(mount(ctx.chrootRootDir.c_str(), ctx.chrootRootDir.c_str(), 0, MS_BIND, 0) == -1)
                throw SysError(format("unable to bind mount ‘%1%’") % ctx.chrootRootDir);
                throw SysError(std::format("unable to bind mount `{}'", ctx.chrootRootDir));
        }
    }
#endif


@@ 526,7 531,7 @@ static void bindMount(Path source, Path target, bool readOnly)
#if HAVE_SYS_MOUNT_H && defined(MS_BIND)
    struct stat st;
    if (lstat(source.c_str(), &st) == -1)
        throw SysError(format("getting attributes of path `%1%'") % source);
        throw SysError(std::format("getting attributes of path `{}'", source));

    if(S_ISDIR(st.st_mode))
        createDirs(target);


@@ 545,11 550,11 @@ static void bindMount(Path source, Path target, bool readOnly)
        while(true){
            if(lstat(target.c_str(), &st2) != -1) {
                if(!S_ISREG(st2.st_mode))
                    throw Error(format("mount target `%1%' exists but is not a regular file") % target);
                    throw Error(std::format("mount target `{}' exists but is not a regular file", target));
                break;
            }
            if(errno != ENOENT) {
                throw SysError(format("stat'ing path `%1%'") % target);
                throw SysError(std::format("stat'ing path `{}'", target));
            }
            AutoCloseFD fd = open(target.c_str(),
                                  O_WRONLY | O_NOFOLLOW | O_CREAT | O_EXCL,


@@ 561,7 566,7 @@ static void bindMount(Path source, Path target, bool readOnly)
            /* Note: because of O_CREAT | O_EXCL, EACCES can only mean a
             * permission issue with the parent directory */
            if(errno != EEXIST)
                throw SysError(format("Creating placeholder regular file target mount `%1%'") % target);
                throw SysError(std::format("Creating placeholder regular file target mount `{}'", target));
        }
    }



@@ 569,7 574,7 @@ static void bindMount(Path source, Path target, bool readOnly)
       are in an unprivileged mount namespace and not specifying MS_REC would
       reveal subtrees that had been covered up. */
    if (mount(source.c_str(), target.c_str(), 0, MS_BIND|MS_REC, 0) == -1)
        throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
        throw SysError(std::format("bind mount from `{}' to `{}' failed", source, target));
    if(readOnly) {
#if defined(MS_REMOUNT) && defined(MS_RDONLY)
        /* Extra flags passed with MS_BIND are ignored, hence the extra


@@ 581,12 586,12 @@ static void bindMount(Path source, Path target, bool readOnly)
#if HAVE_STATVFS
        struct statvfs stvfs;
        if(statvfs(target.c_str(), &stvfs) == -1)
            throw SysError(format("statvfs of `%1%'") % target);
            throw SysError(std::format("statvfs of `{}'", target));
        mount_flags |= statfsToMountFlags(stvfs.f_flag);
#endif

        if (mount(source.c_str(), target.c_str(), 0, mount_flags, 0) == -1)
            throw SysError(format("read-only remount of `%1%' failed") % target);
            throw SysError(std::format("read-only remount of `{}' failed", target));
#else
        throw Error("remounting read-only is not supported on this platform");
#endif


@@ 631,7 636,7 @@ static void mountProcAction(SpawnContext & sctx)
        Path target = (ctx.doChroot ? ctx.chrootRootDir : "") + "/proc";
        createDirs(target);
        if(mount("none", target.c_str(), "proc", 0, 0) == -1)
            throw SysError(format("mounting `%1%'") % target);
            throw SysError(std::format("mounting `{}'", target));
    }
#endif
}


@@ 645,7 650,7 @@ static void mountDevshmAction(SpawnContext & sctx)
        Path target = (ctx.doChroot ? ctx.chrootRootDir : "") + "/dev/shm";
        createDirs(target);
        if(mount("none", target.c_str(), "tmpfs", 0, 0) == -1)
            throw SysError(format("mounting `%1%'") % target);
            throw SysError(std::format("mounting `{}'", target));
    }
#endif
}


@@ 661,13 666,13 @@ static void mountDevptsAction(SpawnContext & sctx)
        if(pathExists(chroot + "/dev/ptmx")) return;
        createDirs(target);
        if(mount("none", target.c_str(), "devpts", 0, "newinstance,mode=0620") == -1)
            throw SysError(format("mounting `%1%'") % target);
            throw SysError(std::format("mounting `{}'", target));
        createSymlink("/dev/pts/ptmx", chroot + "/dev/ptmx");
        /* Make sure /dev/pts/ptmx is world-writable.  With some Linux
           versions, it is created with permissions 0.  */
        Path targetPtmx = chroot + "/dev/pts/ptmx";
        if (chmod(targetPtmx.c_str(), 0666) == -1)
            throw SysError(format("setting permissions on `%1%'") % targetPtmx);
            throw SysError(std::format("setting permissions on `{}'", targetPtmx));
    }
#endif
}


@@ 679,16 684,16 @@ static void pivotRootAction(SpawnContext & sctx)
    CloneSpawnContext & ctx = (CloneSpawnContext &) sctx;
    if((ctx.cloneFlags & CLONE_NEWNS) != 0 && ctx.doChroot) {
        if (chdir(ctx.chrootRootDir.c_str()) == -1)
            throw SysError(format("cannot change directory to '%1%'") % ctx.chrootRootDir);
            throw SysError(std::format("cannot change directory to '{}'", ctx.chrootRootDir));

        if (mkdir("real-root", 0) == -1)
            throw SysError("cannot create real-root directory");

        if (pivot_root(".", "real-root") == -1)
            throw SysError(format("cannot pivot old root directory onto '%1%'") % (ctx.chrootRootDir + "/real-root"));
            throw SysError(std::format("cannot pivot old root directory onto '{}'", (ctx.chrootRootDir + "/real-root")));

        if (chroot(".") == -1)
            throw SysError(format("cannot change root directory to '%1%'") % ctx.chrootRootDir);
            throw SysError(std::format("cannot change root directory to '{}'", ctx.chrootRootDir));

        if (umount2("real-root", MNT_DETACH) == -1)
            throw SysError("cannot unmount real root filesystem");


@@ 761,7 766,7 @@ void unshareAndInitUserns(int flags, const string & uidMap,
                throw SysError("reaping userns init process");
        }
        if(!(WIFEXITED(status) != 0 && WEXITSTATUS(status) == EXIT_SUCCESS))
            throw Error(format("userns init child exited with status %1%") % WEXITSTATUS(status));
            throw Error(std::format("userns init child exited with status {}", WEXITSTATUS(status)));
    }
#endif
}

M nix/libutil/types.hh => nix/libutil/types.hh +6 -15
@@ 5,8 5,9 @@
#include <string>
#include <list>
#include <set>
#include <vector>
#include <string_view>

#include <boost/format.hpp>

/* Before 4.7, gcc's std::exception uses empty throw() specifiers for
 * its (virtual) destructor and what() in c++11 mode, in violation of spec


@@ 26,16 27,6 @@ using std::string;
using std::list;
using std::set;
using std::vector;
using boost::format;


struct FormatOrString
{
    string s;
    FormatOrString(const string & s) : s(s) { };
    FormatOrString(const format & f) : s(f.str()) { };
    FormatOrString(const char * s) : s(s) { };
};


/* BaseError should generally not be caught, as it has Interrupted as


@@ 47,7 38,7 @@ protected:
    string err;
public:
    unsigned int status; // exit status
    BaseError(const FormatOrString & fs, unsigned int status = 1);
    BaseError(std::string_view fs, unsigned int status = 1);
#ifdef EXCEPTION_NEEDS_THROW_SPEC
    ~BaseError() throw () { };
    const char * what() const throw () { return err.c_str(); }


@@ 56,14 47,14 @@ public:
#endif
    const string & msg() const { return err; }
    const string & prefix() const { return prefix_; }
    BaseError & addPrefix(const FormatOrString & fs);
    BaseError & addPrefix(std::string fs);
};

#define MakeError(newClass, superClass) \
    class newClass : public superClass                  \
    {                                                   \
    public:                                             \
        newClass(const FormatOrString & fs, unsigned int status = 1) : superClass(fs, status) { }; \
        newClass(std::string_view fs, unsigned int status = 1) : superClass(fs, status) { }; \
    };

MakeError(Error, BaseError)


@@ 72,7 63,7 @@ class SysError : public Error
{
public:
    int errNo;
    SysError(const FormatOrString & fs);
    SysError(std::string_view fs);
};



M nix/libutil/util.cc => nix/libutil/util.cc +82 -80
@@ 9,6 9,8 @@
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <cassert>
#include <format>

#include <sys/wait.h>
#include <unistd.h>


@@ 35,22 37,22 @@ extern char * * environ;
namespace nix {


BaseError::BaseError(const FormatOrString & fs, unsigned int status)
BaseError::BaseError(std::string_view fs, unsigned int status)
    : status(status)
{
    err = fs.s;
    err = fs;
}


BaseError & BaseError::addPrefix(const FormatOrString & fs)
BaseError & BaseError::addPrefix(std::string fs)
{
    prefix_ = fs.s + prefix_;
    prefix_ = fs + prefix_;
    return *this;
}


SysError::SysError(const FormatOrString & fs)
    : Error(format("%1%: %2%") % fs.s % strerror(errno))
SysError::SysError(std::string_view fs)
    : Error(std::format("{}: {}", fs, strerror(errno)))
    , errNo(errno)
{
}


@@ 114,7 116,7 @@ Path canonPath(const Path & path, bool resolveSymlinks)
    string s;

    if (path[0] != '/')
        throw Error(format("not an absolute path: `%1%'") % path);
        throw Error(std::format("not an absolute path: `{}'", path));

    string::const_iterator i = path.begin(), end = path.end();
    string temp;


@@ 150,7 152,7 @@ Path canonPath(const Path & path, bool resolveSymlinks)
               the symlink target might contain new symlinks). */
            if (resolveSymlinks && isLink(s)) {
                if (++followCount >= maxFollow)
                    throw Error(format("infinite symlink recursion in path `%1%'") % path);
                    throw Error(std::format("infinite symlink recursion in path `{}'", path));
                temp = absPath(readLink(s), dirOf(s))
                    + string(i, end);
                i = temp.begin(); /* restart */


@@ 168,7 170,7 @@ Path dirOf(const Path & path)
{
    Path::size_type pos = path.rfind('/');
    if (pos == string::npos)
        throw Error(format("invalid file name `%1%'") % path);
        throw Error(std::format("invalid file name `{}'", path));
    return pos == 0 ? "/" : Path(path, 0, pos);
}



@@ 177,7 179,7 @@ string baseNameOf(const Path & path)
{
    Path::size_type pos = path.rfind('/');
    if (pos == string::npos)
        throw Error(format("invalid file name `%1%'") % path);
        throw Error(std::format("invalid file name `{}'", path));
    return string(path, pos + 1);
}



@@ 195,7 197,7 @@ struct stat lstat(const Path & path)
{
    struct stat st;
    if (lstat(path.c_str(), &st))
        throw SysError(format("getting status of `%1%'") % path);
        throw SysError(std::format("getting status of `{}'", path));
    return st;
}



@@ 212,7 214,7 @@ bool pathExists(const Path & path)
#endif
    if (!res) return true;
    if (errno != ENOENT && errno != ENOTDIR)
        throw SysError(format("getting status of %1%") % path);
        throw SysError(std::format("getting status of {}", path));
    return false;
}



@@ 222,14 224,14 @@ Path readLink(const Path & path)
    checkInterrupt();
    struct stat st = lstat(path);
    if (!S_ISLNK(st.st_mode))
        throw Error(format("`%1%' is not a symlink") % path);
        throw Error(std::format("`{}' is not a symlink", path));
    std::vector<char> buf(st.st_size);
    ssize_t rlsize = readlink(path.c_str(), buf.data(), st.st_size);
    if (rlsize == -1)
        throw SysError(format("reading symbolic link '%1%'") % path);
        throw SysError(std::format("reading symbolic link '{}'", path));
    else if (rlsize > st.st_size)
        throw Error(format("symbolic link ‘%1%’ size overflow %2% > %3%")
            % path % rlsize % st.st_size);
        throw Error(std::format("symbolic link `{}' size overflow {} > {}",
            path, rlsize, st.st_size));
    return string(buf.begin(), buf.end());
}



@@ 253,7 255,7 @@ static DirEntries readDirectory(DIR *dir)
        if (name == "." || name == "..") continue;
        entries.emplace_back(name, dirent->d_ino, dirent->d_type);
    }
    if (errno) throw SysError(format("reading directory"));
    if (errno) throw SysError("reading directory");

    return entries;
}


@@ 261,7 263,7 @@ static DirEntries readDirectory(DIR *dir)
DirEntries readDirectory(const Path & path)
{
    AutoCloseDir dir = opendir(path.c_str());
    if (!dir) throw SysError(format("opening directory `%1%'") % path);
    if (!dir) throw SysError(std::format("opening directory `{}'", path));
    return readDirectory(dir);
}



@@ 273,7 275,7 @@ static DirEntries readDirectory(int fd)
    if (fdcopy < 0) throw SysError("dup");

    AutoCloseDir dir = fdopendir(fdcopy);
    if (!dir) throw SysError(format("opening directory from file descriptor `%1%'") % fd);
    if (!dir) throw SysError(std::format("opening directory from file descriptor `{}'", fd));
    return readDirectory(dir);
}



@@ 304,7 306,7 @@ string readFile(const Path & path, bool drain)
{
    AutoCloseFD fd = open(path.c_str(), O_RDONLY);
    if (fd == -1)
        throw SysError(format("reading file `%1%'") % path);
        throw SysError(std::format("reading file `{}'", path));
    return drain ? drainFD(fd) : readFile(fd);
}



@@ 313,7 315,7 @@ void writeFile(const Path & path, const string & s)
{
    AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
    if (fd == -1)
        throw SysError(format("writing file '%1%'") % path);
        throw SysError(std::format("writing file '{}'", path));
    writeFull(fd, s);
}



@@ 349,7 351,7 @@ static void _deletePathAt(int fd, const Path & path, const Path & fullPath, unsi
{
    checkInterrupt();

    printMsg(lvlVomit, format("%1%") % fullPath);
    printMsg(lvlVomit, std::format("{}", fullPath));

#ifdef HAVE_STATX
# define st_mode stx_mode


@@ 364,7 366,7 @@ static void _deletePathAt(int fd, const Path & path, const Path & fullPath, unsi
    struct stat st;
#endif
    if (fstatat(fd, path.c_str(), &st, AT_SYMLINK_NOFOLLOW))
        throw SysError(format("getting status of `%1%'") % fullPath);
        throw SysError(std::format("getting status of `{}'", fullPath));

    /* Note: if another process modifies what is at 'path' between now and
       when we actually delete it, this may be inaccurate, but I know of no


@@ 380,17 382,17 @@ static void _deletePathAt(int fd, const Path & path, const Path & fullPath, unsi
                                 O_NOFOLLOW |
                                 O_CLOEXEC);
      if(!dirfd.isOpen())
        throw SysError(format("opening `%1%'") % fullPath);
        throw SysError(std::format("opening `{}'", fullPath));

      /* st.st_mode may currently be from a different file than what we
         actually opened, get it straight from the file instead */
      if(fstat(dirfd, &st))
        throw SysError(format("re-getting status of `%1'") % fullPath);
        throw SysError(std::format("re-getting status of `{}'", fullPath));

      /* Make the directory writable. */
      if (!(st.st_mode & S_IWUSR)) {
        if (fchmod(dirfd, st.st_mode | S_IWUSR) == -1)
          throw SysError(format("making `%1%' writable") % fullPath);
          throw SysError(std::format("making `{}' writable", fullPath));
      }

      for (auto & i : readDirectory(dirfd))


@@ 400,7 402,7 @@ static void _deletePathAt(int fd, const Path & path, const Path & fullPath, unsi
    int ret;
    ret = unlinkat(fd, path.c_str(), S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0 );
    if (ret == -1)
        throw SysError(format("cannot unlink `%1%'") % fullPath);
        throw SysError(std::format("cannot unlink `{}'", fullPath));

#undef st_mode
#undef st_size


@@ 425,7 427,7 @@ void deletePath(const Path & path)
void deletePath(const Path & path, unsigned long long & bytesFreed, size_t linkThreshold)
{
    startNest(nest, lvlDebug,
        format("recursively deleting path `%1%'") % path);
        std::format("recursively deleting path `{}'", path));
    bytesFreed = 0;
    _deletePath(path, bytesFreed, linkThreshold);
}


@@ 447,14 449,14 @@ static void copyFile(int sourceFd, int destinationFd)
	}
    } else {
	if (result < 0)
	    throw SysError(format("copy_file_range `%1%' to `%2%'") % sourceFd % destinationFd);
	    throw SysError(std::format("copy_file_range `{}' to `{}'", sourceFd, destinationFd));

	/* If 'copy_file_range' copied less than requested, try again.  */
	for (ssize_t copied = result; copied < st.st_size; copied += result) {
	    result = copy_file_range(sourceFd, NULL, destinationFd, NULL,
				     st.st_size - copied, 0);
	    if (result < 0)
		throw SysError(format("copy_file_range `%1%' to `%2%'") % sourceFd % destinationFd);
		throw SysError(std::format("copy_file_range `{}' to `{}'", sourceFd, destinationFd));
	}
    }
}


@@ 465,18 467,18 @@ static void copyFileRecursively(int sourceroot, const Path &source,
{
    struct stat st;
    if (fstatat(sourceroot, source.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1)
	throw SysError(format("statting file `%1%'") % source);
	throw SysError(std::format("statting file `{}'", source));

    if (S_ISREG(st.st_mode)) {
	AutoCloseFD sourceFd = openat(sourceroot, source.c_str(),
				      O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
	if (sourceFd == -1) throw SysError(format("opening `%1%'") % source);
	if (sourceFd == -1) throw SysError(std::format("opening `{}'", source));

	AutoCloseFD destinationFd = openat(destinationroot, destination.c_str(),
					   O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC
					   | O_NOFOLLOW | O_EXCL,
					   st.st_mode);
	if (destinationFd == -1) throw SysError(format("opening `%1%'") % source);
	if (destinationFd == -1) throw SysError(std::format("opening `{}'", source));

	copyFile(sourceFd, destinationFd);
	fchown(destinationFd, st.st_uid, st.st_gid);


@@ 487,37 489,37 @@ static void copyFileRecursively(int sourceroot, const Path &source,
	target[st.st_size] = '\0';
	int err = symlinkat(target.data(), destinationroot, destination.c_str());
	if (err != 0)
	    throw SysError(format("creating symlink `%1%'") % destination);
	    throw SysError(std::format("creating symlink `{}'", destination));
	fchownat(destinationroot, destination.c_str(),
		 st.st_uid, st.st_gid, AT_SYMLINK_NOFOLLOW);
    } else if (S_ISDIR(st.st_mode)) {
	int err = mkdirat(destinationroot, destination.c_str(), 0755);
	if (err != 0)
	    throw SysError(format("creating directory `%1%'") % destination);
	    throw SysError(std::format("creating directory `{}'", destination));

	AutoCloseFD destinationFd = openat(destinationroot, destination.c_str(),
					   O_CLOEXEC | O_RDONLY | O_DIRECTORY
					   | O_NOFOLLOW);
	if (err != 0)
	    throw SysError(format("opening directory `%1%'") % destination);
	    throw SysError(std::format("opening directory `{}'", destination));

	AutoCloseFD sourceFd = openat(sourceroot, source.c_str(),
				      O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
	if (sourceFd == -1)
	    throw SysError(format("opening `%1%'") % source);
	    throw SysError(std::format("opening `{}'", source));

        if (deleteSource && !(st.st_mode & S_IWUSR)) {
	    /* Ensure the directory is writable so files within it can be
	       deleted.  */
            if (fchmod(sourceFd, st.st_mode | S_IWUSR) == -1)
                throw SysError(format("making `%1%' directory writable") % source);
                throw SysError(std::format("making `{}' directory writable", source));
        }

        for (auto & i : readDirectory(sourceFd))
	    copyFileRecursively((int)sourceFd, i.name, (int)destinationFd, i.name,
				deleteSource);
	fchown(destinationFd, st.st_uid, st.st_gid);
    } else throw Error(format("refusing to copy irregular file `%1%'") % source);
    } else throw Error(std::format("refusing to copy irregular file `{}'", source));

    if (deleteSource)
	unlinkat(sourceroot, source.c_str(),


@@ 534,9 536,9 @@ static Path tempName(Path tmpRoot, const Path & prefix, bool includePid,
{
    tmpRoot = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true);
    if (includePid)
        return (format("%1%/%2%-%3%-%4%") % tmpRoot % prefix % getpid() % counter++).str();
        return std::format("{}/{}-{}-{}", tmpRoot, prefix, getpid(), counter++);
    else
        return (format("%1%/%2%-%3%") % tmpRoot % prefix % counter++).str();
        return std::format("{}/{}-{}", tmpRoot, prefix, counter++);
}




@@ 560,11 562,11 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
               "wheel", then "tar" will fail to unpack archives that
               have the setgid bit set on directories. */
            if (chown(tmpDir.c_str(), (uid_t) -1, getegid()) != 0)
                throw SysError(format("setting group of directory `%1%'") % tmpDir);
                throw SysError(std::format("setting group of directory `{}'", tmpDir));
            return tmpDir;
        }
        if (errno != EEXIST)
            throw SysError(format("creating directory `%1%'") % tmpDir);
            throw SysError(std::format("creating directory `{}'", tmpDir));
    }
}



@@ 578,15 580,15 @@ Paths createDirs(const Path & path)
    if (lstat(path.c_str(), &st) == -1) {
        created = createDirs(dirOf(path));
        if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST)
            throw SysError(format("creating directory `%1%'") % path);
            throw SysError(std::format("creating directory `{}'", path));
        st = lstat(path);
        created.push_back(path);
    }

    if (S_ISLNK(st.st_mode) && stat(path.c_str(), &st) == -1)
        throw SysError(format("statting symlink `%1%'") % path);
        throw SysError(std::format("statting symlink `{}'", path));

    if (!S_ISDIR(st.st_mode)) throw Error(format("`%1%' is not a directory") % path);
    if (!S_ISDIR(st.st_mode)) throw Error(std::format("`{}' is not a directory", path));

    return created;
}


@@ 595,7 597,7 @@ Paths createDirs(const Path & path)
void createSymlink(const Path & target, const Path & link)
{
    if (symlink(target.c_str(), link.c_str()))
        throw SysError(format("creating symlink from `%1%' to `%2%'") % link % target);
        throw SysError(std::format("creating symlink from `{}' to `{}'", link, target));
}




@@ 623,12 625,12 @@ static string escVerbosity(Verbosity level)
}


void Nest::open(Verbosity level, const FormatOrString & fs)
void Nest::open(Verbosity level, std::string_view fs)
{
    if (level <= verbosity) {
        if (logType == ltEscapes)
            std::cerr << "\033[" << escVerbosity(level) << "p"
                      << fs.s << "\n";
                      << fs << "\n";
        else
            printMsg_(level, fs);
        nest = true;


@@ 648,7 650,7 @@ void Nest::close()
}


void printMsg_(Verbosity level, const FormatOrString & fs)
void printMsg_(Verbosity level, std::string_view fs)
{
    checkInterrupt();
    if (level > verbosity) return;


@@ 658,15 660,15 @@ void printMsg_(Verbosity level, const FormatOrString & fs)
            prefix += "|   ";
    else if (logType == ltEscapes && level != lvlInfo)
        prefix = "\033[" + escVerbosity(level) + "s";
    string s = (format("%1%%2%\n") % prefix % fs.s).str();
    string s = std::format("{}{}\n", prefix, fs);
    writeToStderr(s);
}


void warnOnce(bool & haveWarned, const FormatOrString & fs)
void warnOnce(bool & haveWarned, std::string_view fs)
{
    if (!haveWarned) {
        printMsg(lvlError, format("warning: %1%") % fs.s);
        printMsg(lvlError, std::format("warning: {}", fs));
        haveWarned = true;
    }
}


@@ 685,7 687,7 @@ void writeToStderr(const string & s)
           write errors in exception handlers to ensure that cleanup
           code runs to completion if the other side of stderr has
           been closed unexpectedly. */
        if (!std::uncaught_exception()) throw;
        if (std::uncaught_exceptions() == 0) throw;
    }
}



@@ 754,8 756,8 @@ void waitForMessage(int fd, const string & message)
    string str(message.length(), '\0');
    readFull(fd, (unsigned char*)str.data(), message.length());
    if (str != message)
	throw Error(format("did not receive message '%1%' on file descriptor %2%")
	    % message % fd);
	throw Error(std::format("did not receive message '{}' on file descriptor {}",
	    message, fd));
}




@@ 777,7 779,7 @@ AutoDelete::~AutoDelete()
                deletePath(path);
            else {
                if (remove(path.c_str()) == -1)
                    throw SysError(format("cannot unlink `%1%'") % path);
                    throw SysError(std::format("cannot unlink `{}'", path));
            }
        }
    } catch (...) {


@@ 848,7 850,7 @@ void AutoCloseFD::close()
    if (fd != -1) {
        if (::close(fd) == -1)
            /* This should never happen. */
            throw SysError(format("closing file descriptor %1%") % fd);
            throw SysError(std::format("closing file descriptor {}", fd));
        fd = -1;
    }
}


@@ 1024,13 1026,13 @@ void Pid::kill(bool quiet)
    if (pid == -1 || pid == 0) return;

    if (!quiet)
        printMsg(lvlError, format("killing process %1%") % pid);
        printMsg(lvlError, std::format("killing process {}", pid));

    /* Send the requested signal to the child.  If it has its own
       process group, send the signal to every process in the child
       process group (which hopefully includes *all* its children). */
    if (::kill(separatePG ? -pid : pid, killSignal) != 0)
        printMsg(lvlError, (SysError(format("killing process %1%") % pid).msg()));
        printMsg(lvlError, (SysError(std::format("killing process {}", pid)).msg()));

    /* Wait until the child dies, disregarding the exit status. */
    int status;


@@ 1038,7 1040,7 @@ void Pid::kill(bool quiet)
        checkInterrupt();
        if (errno != EINTR) {
            printMsg(lvlError,
                (SysError(format("waiting for process %1%") % pid).msg()));
                (SysError(std::format("waiting for process {}", pid)).msg()));
            break;
        }
    }


@@ 1079,7 1081,7 @@ void Pid::setKillSignal(int signal)

void killUser(uid_t uid)
{
    debug(format("killing all processes running under uid `%1%'") % uid);
    debug(std::format("killing all processes running under uid `{}'", uid));

    assert(uid != 0); /* just to be safe... */



@@ 1109,7 1111,7 @@ void killUser(uid_t uid)
#endif
            if (errno == ESRCH) break; /* no more processes */
            if (errno != EINTR)
                throw SysError(format("cannot kill processes for uid `%1%'") % uid);
                throw SysError(std::format("cannot kill processes for uid `{}'", uid));
        }

        _exit(0);


@@ 1121,7 1123,7 @@ void killUser(uid_t uid)
    if (status == SIGKILL) return;
#endif
    if (status != 0)
        throw Error(format("cannot kill processes for uid `%1%': %2%") % uid % statusToString(status));
        throw Error(std::format("cannot kill processes for uid `{}': {}", uid, statusToString(status)));

    /* !!! We should really do some check to make sure that there are
       no processes left running under `uid', but there is no portable


@@ 1193,9 1195,9 @@ string runProgram(Path program, bool searchPath, const Strings & args)
        else
            execv(program.c_str(), stringsToCharPtrs(args_).data());

	int err = errno;
        printMsg(lvlError, format("executing `%1%': %2%") % program % strerror(err));
	_exit(127);
        int err = errno;
        printMsg(lvlError, std::format("executing `{}': {}", program, strerror(err)));
        _exit(127);
    });

    pipe.writeSide.close();


@@ 1205,8 1207,8 @@ string runProgram(Path program, bool searchPath, const Strings & args)
    /* Wait for the child to finish. */
    int status = pid.wait(true);
    if (!statusOk(status))
        throw ExecError(format("program `%1%' %2%")
            % program % statusToString(status));
        throw ExecError(std::format("program `{}' {}",
            program, statusToString(status)));

    return result;
}


@@ 1256,7 1258,7 @@ void _interrupted()
    /* Block user interrupts while an exception is being handled.
       Throwing an exception while another exception is being handled
       kills the program! */
    if (!std::uncaught_exception()) {
    if (std::uncaught_exceptions() == 0) {
        _isInterrupted = 0;
        throw Interrupted("interrupted by the user");
    }


@@ 1319,14 1321,14 @@ string statusToString(int status)
{
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        if (WIFEXITED(status))
            return (format("failed with exit code %1%") % WEXITSTATUS(status)).str();
            return std::format("failed with exit code {}", WEXITSTATUS(status));
        else if (WIFSIGNALED(status)) {
            int sig = WTERMSIG(status);
#if HAVE_STRSIGNAL
            const char * description = strsignal(sig);
            return (format("failed due to signal %1% (%2%)") % sig % description).str();
            return std::format("failed due to signal {} ({})", sig, description);
#else
            return (format("failed due to signal %1%") % sig).str();
            return std::format("failed due to signal {}", sig);
#endif
        }
        else


@@ 1347,12 1349,12 @@ bool hasSuffix(const string & s, const string & suffix)
}


void expect(std::istream & str, const string & s)
void expect(std::istream & str, std::string_view s)
{
    std::vector<char> s2(s.size());
    str.read(s2.data(), s2.size());
    if (string(s2.begin(), s2.end()) != s)
        throw FormatError(format("expected string `%1%'") % s);
        throw FormatError(std::format("expected string `{}'", s));
}




@@ 1411,7 1413,7 @@ void ignoreException()
    try {
        throw;
    } catch (std::exception & e) {
        printMsg(lvlError, format("error (ignored): %1%") % e.what());
        printMsg(lvlError, std::format("error (ignored): {}", e.what()));
    }
}



@@ 1425,7 1427,7 @@ void commonChildInit(Pipe & logPipe)
       that e.g. ssh cannot open /dev/tty) and it doesn't receive
       terminal signals. */
    if (setsid() == -1)
        throw SysError(format("creating a new session"));
        throw SysError("creating a new session");

    /* Close the read end so only the parent holds a reference to it.  */
    logPipe.readSide.close();


@@ 1441,7 1443,7 @@ void commonChildInit(Pipe & logPipe)
    /* Reroute stdin to /dev/null. */
    int fdDevNull = open(pathNullDevice.c_str(), O_RDWR);
    if (fdDevNull == -1)
        throw SysError(format("cannot open `%1%'") % pathNullDevice);
        throw SysError(std::format("cannot open `{}'", pathNullDevice));
    if (dup2(fdDevNull, STDIN_FILENO) == -1)
        throw SysError("cannot dup null device into stdin");
    close(fdDevNull);


@@ 1451,7 1453,7 @@ void commonChildInit(Pipe & logPipe)

Agent::Agent(const string &command, const Strings &args, const std::map<string, string> &env)
{
    debug(format("starting agent '%1%'") % command);
    debug(std::format("starting agent '{}'", command));

    /* Create a pipe to get the output of the child. */
    fromAgent.create();


@@ 1487,7 1489,7 @@ Agent::Agent(const string &command, const Strings &args, const std::map<string, 

        execv(command.c_str(), stringsToCharPtrs(allArgs).data());

        throw SysError(format("executing `%1%'") % command);
        throw SysError(std::format("executing `{}'", command));
    });

    pid.setSeparatePG(true);

M nix/libutil/util.hh => nix/libutil/util.hh +7 -4
@@ 2,6 2,9 @@

#include "types.hh"

#include <sstream>
#include <string_view>

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>


@@ 139,11 142,11 @@ private:
public:
    Nest();
    ~Nest();
    void open(Verbosity level, const FormatOrString & fs);
    void open(Verbosity level, std::string_view fs);
    void close();
};

void printMsg_(Verbosity level, const FormatOrString & fs);
void printMsg_(Verbosity level, std::string_view fs);

#define startNest(varName, level, f) \
    Nest varName; \


@@ 160,7 163,7 @@ void printMsg_(Verbosity level, const FormatOrString & fs);

#define debug(f) printMsg(lvlDebug, f)

void warnOnce(bool & haveWarned, const FormatOrString & fs);
void warnOnce(bool & haveWarned, std::string_view fs);

void writeToStderr(const string & s);



@@ 367,7 370,7 @@ bool hasSuffix(const string & s, const string & suffix);


/* Read string `s' from stream `str'. */
void expect(std::istream & str, const string & s);
void expect(std::istream & str, std::string_view s);

MakeError(FormatError, Error)


M nix/local.mk => nix/local.mk +6 -28
@@ 25,31 25,10 @@
BUILT_SOURCES += %D%/libstore/schema.sql.hh
CLEANFILES += %D%/libstore/schema.sql.hh

noinst_LIBRARIES = libformat.a libutil.a libstore.a

# Use '-std=c++11' for 'std::shared_ptr', 'auto', lambdas, and more.
AM_CXXFLAGS = -Wall -std=c++11

libformat_a_SOURCES =				\
  %D%/boost/format/free_funcs.cc		\
  %D%/boost/format/parsing.cc			\
  %D%/boost/format/format_implementation.cc

libformat_headers =				\
  %D%/boost/throw_exception.hpp			\
  %D%/boost/format.hpp				\
  %D%/boost/assert.hpp				\
  %D%/boost/format/macros_default.hpp		\
  %D%/boost/format/format_fwd.hpp		\
  %D%/boost/format/format_class.hpp		\
  %D%/boost/format/exceptions.hpp		\
  %D%/boost/format/group.hpp			\
  %D%/boost/format/feed_args.hpp		\
  %D%/boost/format/internals_fwd.hpp		\
  %D%/boost/format/internals.hpp

libformat_a_CPPFLAGS =				\
  -I$(top_srcdir)/nix
noinst_LIBRARIES = libutil.a libstore.a

# Use '-std=c++20' for 'std::shared_ptr', 'auto', lambdas, and more.
AM_CXXFLAGS = -Wall -std=c++20

libutil_a_SOURCES =				\
  %D%/libutil/archive.cc			\


@@ 73,7 52,6 @@ libutil_headers =				\
libutil_a_CPPFLAGS =				\
  -I$(top_builddir)/nix				\
  -I$(top_srcdir)/%D%/libutil			\
  $(libformat_a_CPPFLAGS)			\
  $(LIBGCRYPT_CPPFLAGS)

libstore_a_SOURCES =				\


@@ 131,7 109,7 @@ guix_daemon_LDFLAGS = 				\
  $(LIBGCRYPT_LDFLAGS)

guix_daemon_LDADD =				\
  libstore.a libutil.a libformat.a -lz		\
  libstore.a libutil.a -lz		\
  $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS)

guix_daemon_headers =				\


@@ 144,7 122,7 @@ guix_daemon_LDADD += -lbz2
endif HAVE_LIBBZ2

noinst_HEADERS =						\
  $(libformat_headers) $(libutil_headers) $(libstore_headers)	\
  $(libutil_headers) $(libstore_headers)	\
  $(guix_daemon_headers)

%D%/libstore/schema.sql.hh: guix/store/schema.sql

M nix/nix-daemon/guix-daemon.cc => nix/nix-daemon/guix-daemon.cc +24 -23
@@ 37,6 37,7 @@
#include <strings.h>
#include <exception>
#include <iostream>
#include <format>

#include <libintl.h>
#include <locale.h>


@@ 187,7 188,7 @@ string_to_bool (const char *arg, bool dflt = true)
  else if (strcasecmp (arg, "no") == 0)
    return false;
  else
    throw nix::Error (format ("'%1%': invalid Boolean value") % arg);
    throw nix::Error(std::format("'{}': invalid Boolean value", arg));
}

/* Parse a single option. */


@@ 201,10 202,8 @@ parse_opt (int key, char *arg, struct argp_state *state)
      break;
    case GUIX_OPT_CHROOT_DIR:
      {
	std::string chroot_dirs;
	std::string chroot_dirs {settings.get("build-extra-chroot-dirs", "")};

	chroot_dirs = settings.get ("build-extra-chroot-dirs",
				    (std::string) "");
	if (chroot_dirs == "")
	  chroot_dirs = arg;
	else


@@ 337,7 336,8 @@ open_unix_domain_socket (const char *file)
  struct sockaddr_un addr;
  addr.sun_family = AF_UNIX;
  if (fileRel.size () >= sizeof (addr.sun_path))
    throw Error (format (_("socket file name '%1%' is too long")) % fileRel);
    throw Error (std::vformat( (_("socket file name '%1%' is too long")), 
      std::make_format_args(fileRel)));
  strcpy (addr.sun_path, fileRel.c_str ());

  unlink (file);


@@ 349,13 349,15 @@ open_unix_domain_socket (const char *file)
  int res = bind (fdSocket, (struct sockaddr *) &addr, sizeof addr);
  umask (oldMode);
  if (res == -1)
    throw SysError (format (_("cannot bind to socket '%1%'")) % file);
    throw SysError (std::vformat( (_("cannot bind to socket '{}'")),
      std::make_format_args(file)));

  if (chdir ("/") == -1) /* back to the root */
    throw SysError (_("cannot change current directory"));

  if (listen (fdSocket, 5) == -1)
    throw SysError (format (_("cannot listen on socket '%1%'")) % file);
    throw SysError (std::vformat( (_("cannot listen on socket '{}'")),
      std::make_format_args(file)));

  return fdSocket.borrow ();
}


@@ 373,7 375,7 @@ open_inet_socket (const struct sockaddr *address, socklen_t length)
    throw SysError (_("cannot bind TCP socket"));

  if (listen (fd, 5) == -1)
    throw SysError (format (_("cannot listen on TCP socket")));
    throw SysError (_("cannot listen on TCP socket"));

  return fd.borrow ();
}


@@ 423,11 425,9 @@ listening_sockets (const std::list<std::string> &options)
				 &hints, &res);

	  if (err != 0)
	    throw Error(format ("failed to look up '%1%': %2%")
			% option % gai_strerror (err));
	    throw Error(std::format("failed to look up '{}': {}", option, gai_strerror(err)));

	  printMsg (lvlDebug, format ("listening on '%1%', port '%2%'")
		    % host % port);
	  printMsg(lvlDebug, std::format("listening on '{}', port '{}'", host, port));

	  /* XXX: Pick the first result, RES.  */
	  result.push_back (open_inet_socket (res->ai_addr,


@@ 530,17 530,19 @@ main (int argc, char *argv[])
	/* We were not "socket-activated" so open the sockets specified by
	   LISTEN_OPTIONS.  */
	sockets = listening_sockets (listen_options);
      else
	printMsg (lvlInfo,
		  format (ngettext ("socket-activated with %1% socket",
				    "socket-activated with %1% sockets",
				    sockets.size ()))
		  % sockets.size ());
      else {
        auto size = sockets.size();
	      printMsg (lvlInfo,
		      std::vformat((ngettext ("socket-activated with %1% socket",
				                  "socket-activated with %1% sockets",
				                  size)),
		        std::make_format_args(size)));
      }

      /* Effect all the changes made via 'settings.set'.  */
      settings.update ();
      printMsg(lvlDebug,
	       format ("build log compression: %1%") % settings.logCompression);
	      std::format("build log compression: {}", int(settings.logCompression)));

      if (geteuid () == 0 && settings.buildUsersGroup.empty ())
	fprintf (stderr, _("warning: daemon is running as root, so \


@@ 553,7 555,7 @@ using `--build-users-group' is highly recommended\n"));
	  chroot_dirs = settings.get ("build-extra-chroot-dirs",
				      (std::string) "");
	  printMsg (lvlDebug,
		    format ("extra chroot directories: '%1%'") % chroot_dirs);
		  std::format("extra chroot directories: '{}'", chroot_dirs));
	}

      if (useDiscover)


@@ 568,9 570,8 @@ using `--build-users-group' is highly recommended\n"));
        });
      }

      printMsg (lvlDebug,
		format ("automatic deduplication set to %1%")
		% settings.autoOptimiseStore);
      printMsg(lvlDebug,
		    std::format("automatic deduplication set to {}", settings.autoOptimiseStore));

      run (sockets);
    }

M nix/nix-daemon/nix-daemon.cc => nix/nix-daemon/nix-daemon.cc +12 -13
@@ 10,8 10,10 @@
#include "builtins.hh"

#include <algorithm>
#include <format>

#include <cstring>
#include <cassert>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>


@@ 640,7 642,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
                    if (pw != NULL)
                        store->createUser(value, pw->pw_uid);
                    else
                        printMsg(lvlInfo, format("user name %1% not found") % value);
                        printMsg(lvlInfo, std::format("user name {} not found", value));
		}
                else
                    settings.set(trusted ? name : "untrusted-" + name, value);


@@ 772,7 774,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
    }

    default:
        throw Error(format("invalid operation %1%") % op);
        throw Error(std::format("invalid operation {}", op));
    }
}



@@ 834,7 836,7 @@ static void processConnection(bool trusted, uid_t userId)
            if (pw != NULL && pw->pw_name != NULL)
                store->createUser(pw->pw_name, userId);
            else
                printMsg(lvlInfo, format("user with UID %1% not found") % userId);
                printMsg(lvlInfo, std::format("user with UID {} not found", userId));
	}

        stopWork();


@@ 882,7 884,7 @@ static void processConnection(bool trusted, uid_t userId)

    canSendStderr = false;
    _isInterrupted = false;
    printMsg(lvlDebug, format("%1% operations") % opCount);
    printMsg(lvlDebug, std::format("{} operations", opCount));
}




@@ 971,9 973,8 @@ static void acceptConnection(int fdSocket)
            struct passwd * pw = getpwuid(cred.uid);
            string user = pw ? pw->pw_name : std::to_string(cred.uid);

	    printMsg(lvlInfo,
		     format((string) "accepted connection from pid %1%, user %2%")
		     % clientPid % user);
	    printMsg(lvlInfo, std::format("accepted connection from pid {}, user {}",
		    clientPid, user));
#endif
	} else {
	    char address_str[128];


@@ 992,9 993,7 @@ static void acceptConnection(int fdSocket)
	    }

	    if (result != NULL) {
		printMsg(lvlInfo,
			 format("accepted connection from %1%")
			 % address_str);
		printMsg(lvlInfo, std::format("accepted connection from {}", address_str));
	    }
	}



@@ 1004,7 1003,7 @@ static void acceptConnection(int fdSocket)

                /* Background the daemon. */
                if (setsid() == -1)
                    throw SysError(format("creating a new session"));
                    throw SysError("creating a new session");

                /* Restore normal handling of SIGCHLD. */
                setSigChldAction(false);


@@ 1033,7 1032,7 @@ static void acceptConnection(int fdSocket)
    } catch (Interrupted & e) {
	throw;
    } catch (Error & e) {
	printMsg(lvlError, format("error processing connection: %1%") % e.msg());
	printMsg(lvlError, std::format("error processing connection: {}", e.msg()));
    }
}



@@ 1072,7 1071,7 @@ static void daemonLoop(const std::vector<int>& sockets)
	    int err = errno;
	    if (err == EINTR)
		continue;
	    throw SysError(format("select error: %1%") % strerror(err));
	    throw SysError(std::format("select error: {}", strerror(err)));
	}

	for (unsigned int i = 0; i < sockets.size(); i++) {