Compare commits
113 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
331ae4228a | ||
|
|
aaee69cfde | ||
|
|
83075304e5 | ||
|
|
1cb030736e | ||
|
|
a01629894d | ||
|
|
40f32ae00a | ||
|
|
8846465934 | ||
|
|
dc14a3de46 | ||
|
|
79ba0431db | ||
|
|
5acb45446e | ||
|
|
ce5fd1cc12 | ||
|
|
dec8fbc52b | ||
|
|
949c4fa1a8 | ||
|
|
f21b341957 | ||
|
|
5d7a20dac3 | ||
|
|
0a0c1fcb4d | ||
|
|
3b521bb1bd | ||
|
|
1a7468a57a | ||
|
|
b75719b984 | ||
|
|
39ce70025b | ||
|
|
9202570f8c | ||
|
|
e877c69d78 | ||
|
|
df648c4967 | ||
|
|
d84931ee56 | ||
|
|
c7bdb76fe4 | ||
|
|
d5ee6f8700 | ||
|
|
2616e6a6f3 | ||
|
|
9f4ad99e92 | ||
|
|
249988a787 | ||
|
|
49231fbe41 | ||
|
|
401452e57a | ||
|
|
7984cfc7c1 | ||
|
|
667a6afb9d | ||
|
|
6f1a0f948d | ||
|
|
ab350eafd2 | ||
|
|
b3fc38bf6a | ||
|
|
71cc3ceae5 | ||
|
|
54664b6fb7 | ||
|
|
335aa1c35d | ||
|
|
6822fd7bf4 | ||
|
|
9d56ca219f | ||
|
|
b9ecadee6e | ||
|
|
c11bbcfd26 | ||
|
|
d41d085b77 | ||
|
|
7b3f44e05b | ||
|
|
f5b6fa5256 | ||
|
|
8898e86b4f | ||
|
|
3509299aca | ||
|
|
135b7d54db | ||
|
|
e6363b05ae | ||
|
|
9c620e4afa | ||
|
|
5304a1eb3a | ||
|
|
73b163c1a1 | ||
|
|
c834a5c597 | ||
|
|
822c072cfa | ||
|
|
9bcc31c941 | ||
|
|
81304a6bb5 | ||
|
|
e5fbf58041 | ||
|
|
8511571f65 | ||
|
|
1d1c3691d2 | ||
|
|
d072485d28 | ||
|
|
5d4b90b689 | ||
|
|
089b436175 | ||
|
|
b96239c657 | ||
|
|
9ebd78144a | ||
|
|
6011bd0da2 | ||
|
|
2b95a9dc05 | ||
|
|
9a99dc736d | ||
|
|
cab3f4977a | ||
|
|
333f4963de | ||
|
|
40274c1f4f | ||
|
|
a279137327 | ||
|
|
85a913a3e7 | ||
|
|
0b38b43bab | ||
|
|
ab644ad10b | ||
|
|
a5a90f501e | ||
|
|
be96c2189c | ||
|
|
5895c160c4 | ||
|
|
609a224848 | ||
|
|
224c585aba | ||
|
|
7952a8053c | ||
|
|
bfa5d77211 | ||
|
|
82e3d8fafe | ||
|
|
f826e432aa | ||
|
|
01b34fe584 | ||
|
|
207ff2caf0 | ||
|
|
40b5936691 | ||
|
|
3da9687854 | ||
|
|
bb03c45ca0 | ||
|
|
d4c3edfaba | ||
|
|
3ec5252582 | ||
|
|
2b07b0e7eb | ||
|
|
692b562342 | ||
|
|
c0cbaef4be | ||
|
|
5f5cab0ac7 | ||
|
|
85effedca3 | ||
|
|
5079ccb455 | ||
|
|
1849aa2a72 | ||
|
|
38e12df631 | ||
|
|
aeaffec785 | ||
|
|
94cf1f86bb | ||
|
|
bc57eb3c8a | ||
|
|
fab7b128b9 | ||
|
|
34fcf5fa0c | ||
|
|
7a96da3627 | ||
|
|
6656993f83 | ||
|
|
a7ab242fb4 | ||
|
|
c739e20585 | ||
|
|
727beb798a | ||
|
|
2f04e7102e | ||
|
|
a09e66da5a | ||
|
|
822794001c | ||
|
|
b9f09b3268 |
@@ -1 +1,3 @@
|
||||
SUBDIRS = src scripts
|
||||
SUBDIRS = externals src scripts corepkgs
|
||||
|
||||
EXTRA_DIST = boost/*.hpp boost/format/*.hpp substitute.mk
|
||||
68
boost/format.hpp
Normal file
68
boost/format.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// -*- 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>
|
||||
|
||||
#include <locale>
|
||||
|
||||
#include <boost/format/macros_default.hpp>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
template<class E> void throw_exception(E const & e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
#define BOOST_ASSERT(expr) assert(expr)
|
||||
|
||||
|
||||
// **** 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
|
||||
96
boost/format/exceptions.hpp
Normal file
96
boost/format/exceptions.hpp
Normal file
@@ -0,0 +1,96 @@
|
||||
// -*- 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 refered 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 refered 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
|
||||
248
boost/format/feed_args.hpp
Normal file
248
boost/format/feed_args.hpp
Normal file
@@ -0,0 +1,248 @@
|
||||
// -*- 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 {
|
||||
|
||||
template<class Tr, class Ch> inline
|
||||
void empty_buf(BOOST_IO_STD basic_ostringstream<Ch,Tr> & os) {
|
||||
static const std::basic_string<Ch, Tr> emptyStr;
|
||||
os.str(emptyStr);
|
||||
}
|
||||
|
||||
template<class Ch, class Tr>
|
||||
void do_pad( std::basic_string<Ch,Tr> & s,
|
||||
std::streamsize w,
|
||||
const Ch c,
|
||||
std::ios_base::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_base::left) {
|
||||
s.append(n, c);
|
||||
}
|
||||
else {
|
||||
s.insert(s.begin(), n, c);
|
||||
}
|
||||
}
|
||||
} // -do_pad(..)
|
||||
|
||||
|
||||
template< class Ch, class Tr, class T> inline
|
||||
void put_head(BOOST_IO_STD basic_ostream<Ch, Tr>& , const T& ) {
|
||||
}
|
||||
|
||||
template< class Ch, class Tr, class T> inline
|
||||
void put_head( BOOST_IO_STD basic_ostream<Ch, Tr>& os, const group1<T>& x ) {
|
||||
os << group_head(x.a1_); // send the first N-1 items, not the last
|
||||
}
|
||||
|
||||
template< class Ch, class Tr, class T> inline
|
||||
void put_last( BOOST_IO_STD basic_ostream<Ch, Tr>& os, const T& x ) {
|
||||
os << x ;
|
||||
}
|
||||
|
||||
template< class Ch, class Tr, class T> inline
|
||||
void put_last( BOOST_IO_STD basic_ostream<Ch, Tr>& os, const group1<T>& x ) {
|
||||
os << group_last(x.a1_); // this selects the last element
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST
|
||||
template< class Ch, class Tr, class T> inline
|
||||
void put_head( BOOST_IO_STD basic_ostream<Ch, Tr>& , T& ) {
|
||||
}
|
||||
|
||||
template< class Ch, class Tr, class T> inline
|
||||
void put_last( BOOST_IO_STD basic_ostream<Ch, Tr>& os, T& x ) {
|
||||
os << x ;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
template< class Ch, class Tr, class T>
|
||||
void put( T x,
|
||||
const format_item<Ch, Tr>& specs,
|
||||
std::basic_string<Ch, Tr> & res,
|
||||
BOOST_IO_STD basic_ostringstream<Ch, Tr>& oss_ )
|
||||
{
|
||||
// does the actual conversion of x, with given params, into a string
|
||||
// using the *supplied* strinstream. (the stream state is important)
|
||||
|
||||
typedef std::basic_string<Ch, Tr> string_t;
|
||||
typedef format_item<Ch, Tr> format_item_t;
|
||||
|
||||
stream_format_state<Ch, Tr> 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_base::fmtflags fl=oss_.flags();
|
||||
const bool internal = (fl & std::ios_base::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 Ch, class Tr, class T>
|
||||
void distribute(basic_format<Ch,Tr>& 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<Ch, Tr, T> (x, self.items_[i], self.items_[i].res_, self.oss_ );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class Ch, class Tr, class T>
|
||||
basic_format<Ch, Tr>& feed(basic_format<Ch,Tr>& self, T x)
|
||||
{
|
||||
if(self.dumped_) self.clear();
|
||||
distribute<Ch, Tr, 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
|
||||
140
boost/format/format_class.hpp
Normal file
140
boost/format/format_class.hpp
Normal file
@@ -0,0 +1,140 @@
|
||||
// -*- 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 {
|
||||
|
||||
template<class Ch, class Tr>
|
||||
class basic_format
|
||||
{
|
||||
public:
|
||||
typedef Ch CharT; // those 2 are necessary for borland compatibilty,
|
||||
typedef Tr Traits; // in the body of the operator% template.
|
||||
|
||||
|
||||
typedef std::basic_string<Ch, Tr> string_t;
|
||||
typedef BOOST_IO_STD basic_ostringstream<Ch, Tr> internal_stream_t;
|
||||
private:
|
||||
typedef BOOST_IO_STD basic_ostream<Ch, Tr> stream_t;
|
||||
typedef io::detail::stream_format_state<Ch, Tr> stream_format_state;
|
||||
typedef io::detail::format_item<Ch, Tr> format_item_t;
|
||||
|
||||
public:
|
||||
basic_format(const Ch* str);
|
||||
basic_format(const string_t& s);
|
||||
#ifndef BOOST_NO_STD_LOCALE
|
||||
basic_format(const Ch* 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<CharT, Traits, const T&>(*this,x);
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST
|
||||
template<class T> basic_format& operator%(T& x)
|
||||
{
|
||||
return io::detail::feed<CharT, Traits, 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 basic_ostream<Ch, Tr>&
|
||||
operator<< <Ch, Tr> ( BOOST_IO_STD basic_ostream<Ch, Tr>& , const basic_format& );
|
||||
|
||||
|
||||
template<class Ch2, class Tr2, class T> friend basic_format<Ch2, Tr2>&
|
||||
io::detail::feed(basic_format<Ch2,Tr2>&, T);
|
||||
|
||||
template<class Ch2, class Tr2, class T> friend
|
||||
void io::detail::distribute(basic_format<Ch2,Tr2>&, T);
|
||||
|
||||
template<class Ch2, class Tr2, class T> friend
|
||||
basic_format<Ch2, Tr2>& io::detail::modify_item_body(basic_format<Ch2, Tr2>&, int, const T&);
|
||||
|
||||
template<class Ch2, class Tr2, class T> friend
|
||||
basic_format<Ch2, Tr2>& io::detail::bind_arg_body(basic_format<Ch2, Tr2>&, 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
|
||||
55
boost/format/format_fwd.hpp
Normal file
55
boost/format/format_fwd.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
// -*- 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 {
|
||||
|
||||
template<class charT, class Traits = BOOST_IO_STD char_traits<charT> > class basic_format;
|
||||
|
||||
typedef basic_format<char > format;
|
||||
|
||||
#if !defined(BOOST_NO_STD_WSTRING) && !defined(BOOST_NO_STD_WSTREAMBUF)
|
||||
typedef basic_format<wchar_t > wformat;
|
||||
#endif
|
||||
|
||||
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
|
||||
template<class Ch, class Tr>
|
||||
std::basic_string<Ch, Tr> str(const basic_format<Ch, Tr>& ) ;
|
||||
|
||||
} // namespace io
|
||||
|
||||
|
||||
template< class Ch, class Tr>
|
||||
BOOST_IO_STD basic_ostream<Ch, Tr>&
|
||||
operator<<( BOOST_IO_STD basic_ostream<Ch, Tr>&, const basic_format<Ch, Tr>&);
|
||||
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_FORMAT_FWD_HPP
|
||||
268
boost/format/format_implementation.hpp
Normal file
268
boost/format/format_implementation.hpp
Normal file
@@ -0,0 +1,268 @@
|
||||
// -*- 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/format_class.hpp>
|
||||
|
||||
namespace boost {
|
||||
|
||||
// -------- format:: -------------------------------------------
|
||||
template< class Ch, class Tr>
|
||||
basic_format<Ch, Tr> ::basic_format(const Ch* 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
|
||||
template< class Ch, class Tr>
|
||||
basic_format<Ch, Tr> ::basic_format(const Ch* 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 );
|
||||
}
|
||||
|
||||
template< class Ch, class Tr>
|
||||
basic_format<Ch, Tr> ::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
|
||||
|
||||
template< class Ch, class Tr>
|
||||
basic_format<Ch, Tr> ::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);
|
||||
}
|
||||
|
||||
template< class Ch, class Tr>
|
||||
basic_format<Ch, Tr> :: 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_);
|
||||
}
|
||||
|
||||
template< class Ch, class Tr>
|
||||
basic_format<Ch, Tr>& basic_format<Ch, Tr> ::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;
|
||||
}
|
||||
|
||||
|
||||
template< class Ch, class Tr>
|
||||
unsigned char basic_format<Ch,Tr> ::exceptions() const
|
||||
{
|
||||
return exceptions_;
|
||||
}
|
||||
|
||||
template< class Ch, class Tr>
|
||||
unsigned char basic_format<Ch,Tr> ::exceptions(unsigned char newexcept)
|
||||
{
|
||||
unsigned char swp = exceptions_;
|
||||
exceptions_ = newexcept;
|
||||
return swp;
|
||||
}
|
||||
|
||||
|
||||
template< class Ch, class Tr>
|
||||
basic_format<Ch,Tr>& basic_format<Ch,Tr> ::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;
|
||||
}
|
||||
|
||||
template< class Ch, class Tr>
|
||||
basic_format<Ch,Tr>& basic_format<Ch,Tr> ::clear_binds()
|
||||
// cancel all bindings, and clear()
|
||||
{
|
||||
bound_.resize(0);
|
||||
clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template< class Ch, class Tr>
|
||||
basic_format<Ch,Tr>& basic_format<Ch,Tr> ::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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template< class Ch, class Tr>
|
||||
std::basic_string<Ch,Tr> basic_format<Ch,Tr> ::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 Ch, class Tr, class T>
|
||||
basic_format<Ch, Tr>& bind_arg_body( basic_format<Ch, Tr>& 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 Ch, class Tr, class T>
|
||||
basic_format<Ch, Tr>& modify_item_body( basic_format<Ch, Tr>& 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
|
||||
72
boost/format/free_funcs.hpp
Normal file
72
boost/format/free_funcs.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
// -*- 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/format_class.hpp"
|
||||
//#include "boost/throw_exception.hpp"
|
||||
|
||||
namespace boost {
|
||||
|
||||
namespace io {
|
||||
template<class Ch, class Tr> inline
|
||||
std::basic_string<Ch, Tr> str(const basic_format<Ch, Tr>& f)
|
||||
// adds up all pieces of strings and converted items, and return the formatted string
|
||||
{
|
||||
return f.str();
|
||||
}
|
||||
} // - namespace io
|
||||
|
||||
template< class Ch, class Tr>
|
||||
BOOST_IO_STD basic_ostream<Ch, Tr>&
|
||||
operator<<( BOOST_IO_STD basic_ostream<Ch, Tr>& os,
|
||||
const boost::basic_format<Ch, Tr>& f)
|
||||
// effect: "return os << str(f);" but we can try to do it faster
|
||||
{
|
||||
typedef boost::basic_format<Ch, Tr> 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 typename 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
|
||||
680
boost/format/group.hpp
Normal file
680
boost/format/group.hpp
Normal file
@@ -0,0 +1,680 @@
|
||||
|
||||
// -*- 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 basic_ostream<Ch, Tr>&
|
||||
operator << ( BOOST_IO_STD basic_ostream<Ch, Tr>& 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 basic_ostream<Ch, Tr>&
|
||||
operator << (BOOST_IO_STD basic_ostream<Ch, Tr>& 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 basic_ostream<Ch, Tr>&
|
||||
operator << (BOOST_IO_STD basic_ostream<Ch, Tr>& 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 basic_ostream<Ch, Tr>&
|
||||
operator << (BOOST_IO_STD basic_ostream<Ch, Tr>& 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 basic_ostream<Ch, Tr>&
|
||||
operator << (BOOST_IO_STD basic_ostream<Ch, Tr>& 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 basic_ostream<Ch, Tr>&
|
||||
operator << (BOOST_IO_STD basic_ostream<Ch, Tr>& 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 basic_ostream<Ch, Tr>&
|
||||
operator << (BOOST_IO_STD basic_ostream<Ch, Tr>& 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 basic_ostream<Ch, Tr>&
|
||||
operator << (BOOST_IO_STD basic_ostream<Ch, Tr>& 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 basic_ostream<Ch, Tr>&
|
||||
operator << (BOOST_IO_STD basic_ostream<Ch, Tr>& 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 basic_ostream<Ch, Tr>&
|
||||
operator << (BOOST_IO_STD basic_ostream<Ch, Tr>& 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 basic_ostream<Ch, Tr>&
|
||||
operator << (BOOST_IO_STD basic_ostream<Ch, Tr>& 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
|
||||
169
boost/format/internals.hpp
Normal file
169
boost/format/internals.hpp
Normal file
@@ -0,0 +1,169 @@
|
||||
// -*- 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
|
||||
|
||||
template<class Ch, class Tr>
|
||||
struct stream_format_state
|
||||
{
|
||||
typedef BOOST_IO_STD basic_ios<Ch, Tr> basic_ios;
|
||||
|
||||
std::streamsize width_;
|
||||
std::streamsize precision_;
|
||||
Ch fill_;
|
||||
std::ios_base::fmtflags flags_;
|
||||
|
||||
stream_format_state() : width_(-1), precision_(-1), fill_(0), flags_(std::ios_base::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<Ch, Tr, 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
|
||||
|
||||
template<class Ch, class Tr>
|
||||
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 basic_ios<Ch, Tr> basic_ios;
|
||||
typedef detail::stream_format_state<Ch, Tr> stream_format_state;
|
||||
typedef std::basic_string<Ch, Tr> string_t;
|
||||
typedef BOOST_IO_STD basic_ostringstream<Ch, Tr> 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:: -------------------------------------------
|
||||
template<class Ch, class Tr> inline
|
||||
void stream_format_state<Ch,Tr> ::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_);
|
||||
}
|
||||
|
||||
template<class Ch, class Tr> inline
|
||||
void stream_format_state<Ch,Tr> ::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 Ch, class Tr, class T> inline
|
||||
void apply_manip_body( stream_format_state<Ch, Tr>& self,
|
||||
T manipulator)
|
||||
// modify our params according to the manipulator
|
||||
{
|
||||
BOOST_IO_STD basic_stringstream<Ch, Tr> ss;
|
||||
self.apply_on( ss );
|
||||
ss << manipulator;
|
||||
self.set_by_stream( ss );
|
||||
}
|
||||
|
||||
template<class Ch, class Tr> inline
|
||||
void stream_format_state<Ch,Tr> ::reset()
|
||||
// set our params to standard's default state
|
||||
{
|
||||
width_=-1; precision_=-1; fill_=0;
|
||||
flags_ = std::ios_base::dec;
|
||||
}
|
||||
|
||||
|
||||
// --- format_items:: -------------------------------------------
|
||||
template<class Ch, class Tr> inline
|
||||
void format_item<Ch, Tr> ::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_base::left)
|
||||
{
|
||||
pad_scheme_ = pad_scheme_ & (~zeropad); // ignore zeropad in left alignment
|
||||
}
|
||||
else
|
||||
{
|
||||
ref_state_.fill_='0';
|
||||
ref_state_.flags_ |= std::ios_base::internal;
|
||||
}
|
||||
}
|
||||
state_ = ref_state_;
|
||||
}
|
||||
|
||||
|
||||
} } } // namespaces boost :: io :: detail
|
||||
|
||||
|
||||
#endif // BOOST_FORMAT_INTERNALS_HPP
|
||||
65
boost/format/internals_fwd.hpp
Normal file
65
boost/format/internals_fwd.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
// -*- 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 {
|
||||
template<class Ch, class Tr> struct stream_format_state;
|
||||
template<class Ch, class Tr> 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 Ch, class Tr, class T>
|
||||
basic_format<Ch, Tr>& modify_item_body( basic_format<Ch, Tr>& self,
|
||||
int itemN, const T& manipulator);
|
||||
|
||||
template<class Ch, class Tr, class T>
|
||||
basic_format<Ch, Tr>& bind_arg_body( basic_format<Ch, Tr>& self,
|
||||
int argN, const T& val);
|
||||
|
||||
template<class Ch, class Tr, class T>
|
||||
void apply_manip_body( stream_format_state<Ch, Tr>& self,
|
||||
T manipulator);
|
||||
|
||||
// argument feeding (defined in feed_args.hpp ) :
|
||||
template<class Ch, class Tr, class T>
|
||||
void distribute(basic_format<Ch,Tr>& self, T x);
|
||||
|
||||
template<class Ch, class Tr, class T>
|
||||
basic_format<Ch, Tr>& feed(basic_format<Ch,Tr>& self, T x);
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
} // namespace boost
|
||||
|
||||
|
||||
#endif // BOOST_FORMAT_INTERNAL_FWD_HPP
|
||||
48
boost/format/macros_default.hpp
Normal file
48
boost/format/macros_default.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
// -*- 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
|
||||
457
boost/format/parsing.hpp
Normal file
457
boost/format/parsing.hpp
Normal file
@@ -0,0 +1,457 @@
|
||||
// -*- 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/format_class.hpp>
|
||||
//#include <boost/throw_exception.hpp>
|
||||
//#include <boost/assert.hpp>
|
||||
|
||||
|
||||
namespace boost {
|
||||
namespace io {
|
||||
namespace detail {
|
||||
|
||||
template<class Ch, class Stream> inline
|
||||
bool wrap_isdigit(Ch 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, class Ch, class Tr> inline
|
||||
Res str2int(const std::basic_string<Ch, Tr>& s,
|
||||
typename std::basic_string<Ch, Tr>::size_type start,
|
||||
BOOST_IO_STD basic_ios<Ch,Tr> &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 = os.narrow( s[start], 0);
|
||||
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;
|
||||
}
|
||||
|
||||
template<class Ch, class Tr>
|
||||
void skip_asterisk(const std::basic_string<Ch,Tr> & buf,
|
||||
typename std::basic_string<Ch,Tr>::size_type * pos_p,
|
||||
BOOST_IO_STD basic_ios<Ch, Tr> &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]==os.widen('*')) {
|
||||
++ (*pos_p);
|
||||
while (*pos_p < buf.size() && wrap_isdigit(buf[*pos_p],os)) ++(*pos_p);
|
||||
if(buf[*pos_p]==os.widen('$')) ++(*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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class Ch, class Tr>
|
||||
bool parse_printf_directive(const std::basic_string<Ch, Tr> & buf,
|
||||
typename std::basic_string<Ch, Tr>::size_type * pos_p,
|
||||
detail::format_item<Ch, Tr> * fpar,
|
||||
BOOST_IO_STD basic_ios<Ch,Tr> &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<Ch, Tr> format_item_t;
|
||||
BOOST_ASSERT( pos_p != 0);
|
||||
typename std::basic_string<Ch, Tr>::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]==os.widen('|'))
|
||||
{
|
||||
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]==os.widen('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] == os.widen('%') )
|
||||
{
|
||||
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]==os.widen('$') )
|
||||
{
|
||||
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 (os.narrow(buf[i1], 0))
|
||||
{
|
||||
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_base::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_base::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_base::showpoint | std::ios_base::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]==os.widen('.'))
|
||||
{
|
||||
++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]==os.widen('l') || buf[i1]==os.widen('L') || buf[i1]==os.widen('h')) )
|
||||
++i1;
|
||||
if( i1>=buf.size()) {
|
||||
maybe_throw_exception(exceptions);
|
||||
return true;
|
||||
}
|
||||
|
||||
if( in_brackets && buf[i1]==os.widen('|') )
|
||||
{
|
||||
++i1;
|
||||
return true;
|
||||
}
|
||||
switch (os.narrow(buf[i1], 0) )
|
||||
{
|
||||
case 'X':
|
||||
fpar->ref_state_.flags_ |= std::ios_base::uppercase;
|
||||
case 'p': // pointer => set hex.
|
||||
case 'x':
|
||||
fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
|
||||
fpar->ref_state_.flags_ |= std::ios_base::hex;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
|
||||
fpar->ref_state_.flags_ |= std::ios_base::oct;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
fpar->ref_state_.flags_ |= std::ios_base::uppercase;
|
||||
case 'e':
|
||||
fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
|
||||
fpar->ref_state_.flags_ |= std::ios_base::scientific;
|
||||
|
||||
fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
|
||||
fpar->ref_state_.flags_ |= std::ios_base::dec;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
fpar->ref_state_.flags_ &= ~std::ios_base::floatfield;
|
||||
fpar->ref_state_.flags_ |= std::ios_base::fixed;
|
||||
case 'u':
|
||||
case 'd':
|
||||
case 'i':
|
||||
fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
|
||||
fpar->ref_state_.flags_ |= std::ios_base::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_ = os.widen(' ');
|
||||
fpar->pad_scheme_ |= format_item_t::tabulation;
|
||||
fpar->argN_ = format_item_t::argN_tabulation;
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
fpar->ref_state_.flags_ |= std::ios_base::uppercase;
|
||||
break;
|
||||
case 'g': // 'g' conversion is default for floats.
|
||||
fpar->ref_state_.flags_ &= ~std::ios_base::basefield;
|
||||
fpar->ref_state_.flags_ |= std::ios_base::dec;
|
||||
|
||||
// CLEAR all floatield flags, so stream will CHOOSE
|
||||
fpar->ref_state_.flags_ &= ~std::ios_base::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]==os.widen('|') )
|
||||
{
|
||||
++i1;
|
||||
return true;
|
||||
}
|
||||
else maybe_throw_exception(exceptions);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // detail namespace
|
||||
} // io namespace
|
||||
|
||||
|
||||
// -----------------------------------------------
|
||||
// format :: parse(..)
|
||||
|
||||
template<class Ch, class Traits>
|
||||
void basic_format<Ch, Traits> ::parse(const string_t & buf)
|
||||
// parse the format-string
|
||||
{
|
||||
using namespace std;
|
||||
const Ch arg_mark = oss_.widen('%');
|
||||
bool ordered_args=true;
|
||||
int max_argN=-1;
|
||||
typename 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;
|
||||
typename 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
|
||||
10
configure.ac
10
configure.ac
@@ -1,4 +1,4 @@
|
||||
AC_INIT(nix, 0.1)
|
||||
AC_INIT(nix, 0.2pre1)
|
||||
AC_CONFIG_SRCDIR(src/nix.cc)
|
||||
AC_CONFIG_AUX_DIR(config)
|
||||
AM_INIT_AUTOMAKE
|
||||
@@ -9,6 +9,12 @@ AC_CANONICAL_HOST
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_PROG_RANLIB
|
||||
|
||||
AC_CONFIG_FILES([Makefile src/Makefile scripts/Makefile])
|
||||
AC_CHECK_LIB(pthread, pthread_mutex_init)
|
||||
|
||||
AM_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_FILES([Makefile externals/Makefile src/Makefile scripts/Makefile
|
||||
corepkgs/Makefile corepkgs/fetchurl/Makefile
|
||||
corepkgs/nar/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
1
corepkgs/Makefile.am
Normal file
1
corepkgs/Makefile.am
Normal file
@@ -0,0 +1 @@
|
||||
SUBDIRS = fetchurl nar
|
||||
10
corepkgs/fetchurl/Makefile.am
Normal file
10
corepkgs/fetchurl/Makefile.am
Normal file
@@ -0,0 +1,10 @@
|
||||
all-local: fetchurl.sh
|
||||
|
||||
install-exec-local:
|
||||
$(INSTALL) -d $(datadir)/fix/fetchurl
|
||||
$(INSTALL_DATA) fetchurl.fix $(datadir)/fix/fetchurl
|
||||
$(INSTALL_PROGRAM) fetchurl.sh $(datadir)/fix/fetchurl
|
||||
|
||||
include ../../substitute.mk
|
||||
|
||||
EXTRA_DIST = fetchurl.fix fetchurl.sh
|
||||
10
corepkgs/fetchurl/fetchurl.fix
Normal file
10
corepkgs/fetchurl/fetchurl.fix
Normal file
@@ -0,0 +1,10 @@
|
||||
Function(["url", "md5"],
|
||||
Package(
|
||||
[ ("build", Relative("fetchurl/fetchurl.sh"))
|
||||
, ("url", Var("url"))
|
||||
, ("md5", Var("md5"))
|
||||
, ("name", BaseName(Var("url")))
|
||||
, ("id", Var("md5"))
|
||||
]
|
||||
)
|
||||
)
|
||||
10
corepkgs/fetchurl/fetchurl.sh
Normal file
10
corepkgs/fetchurl/fetchurl.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#! /bin/sh
|
||||
|
||||
echo "downloading $url into $out..."
|
||||
wget "$url" -O "$out" || exit 1
|
||||
|
||||
actual=$(md5sum -b $out | cut -c1-32)
|
||||
if ! test "$actual" == "$md5"; then
|
||||
echo "hash is $actual, expected $md5"
|
||||
exit 1
|
||||
fi
|
||||
12
corepkgs/nar/Makefile.am
Normal file
12
corepkgs/nar/Makefile.am
Normal file
@@ -0,0 +1,12 @@
|
||||
all-local: nar.sh unnar.sh
|
||||
|
||||
install-exec-local:
|
||||
$(INSTALL) -d $(datadir)/fix/nar
|
||||
$(INSTALL_DATA) nar.fix $(datadir)/fix/nar
|
||||
$(INSTALL_PROGRAM) nar.sh $(datadir)/fix/nar
|
||||
$(INSTALL_DATA) unnar.fix $(datadir)/fix/nar
|
||||
$(INSTALL_PROGRAM) unnar.sh $(datadir)/fix/nar
|
||||
|
||||
include ../../substitute.mk
|
||||
|
||||
EXTRA_DIST = nar.fix nar.sh unnar.fix unnar.sh
|
||||
8
corepkgs/nar/nar.fix
Normal file
8
corepkgs/nar/nar.fix
Normal file
@@ -0,0 +1,8 @@
|
||||
Function(["path"],
|
||||
Package(
|
||||
[ ("name", "nar")
|
||||
, ("build", Relative("nar/nar.sh"))
|
||||
, ("path", Var("path"))
|
||||
]
|
||||
)
|
||||
)
|
||||
10
corepkgs/nar/nar.sh.in
Normal file
10
corepkgs/nar/nar.sh.in
Normal file
@@ -0,0 +1,10 @@
|
||||
#! /bin/sh
|
||||
|
||||
echo "packing $path into $out..."
|
||||
mkdir $out || exit 1
|
||||
tmp=$out/tmp
|
||||
@bindir@/nix --dump --path "$path" | bzip2 > $out/tmp || exit 1
|
||||
|
||||
md5=$(md5sum -b $tmp | cut -c1-32)
|
||||
if test $? != 0; then exit 1; fi
|
||||
mv $out/tmp $out/$md5-`basename $path`.nar.bz2 || exit 1
|
||||
9
corepkgs/nar/unnar.fix
Normal file
9
corepkgs/nar/unnar.fix
Normal file
@@ -0,0 +1,9 @@
|
||||
Function(["nar", "name"],
|
||||
Package(
|
||||
[ ("name", Var("name"))
|
||||
, ("build", Relative("nar/unnar.sh"))
|
||||
, ("nar", Var("nar"))
|
||||
, ("id", Var("id"))
|
||||
]
|
||||
)
|
||||
)
|
||||
4
corepkgs/nar/unnar.sh.in
Normal file
4
corepkgs/nar/unnar.sh.in
Normal file
@@ -0,0 +1,4 @@
|
||||
#! /bin/sh
|
||||
|
||||
echo "unpacking $nar to $out..."
|
||||
bunzip2 < $nar | @bindir@/nix --restore "$out" || exit 1
|
||||
52
externals/Makefile.am
vendored
Normal file
52
externals/Makefile.am
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# Berkeley DB
|
||||
|
||||
DB = db-4.0.14
|
||||
DB_URL = http://www.sleepycat.com/update/snapshot/db-4.0.14.tar.gz
|
||||
|
||||
$(DB).tar.gz:
|
||||
wget $(DB_URL)
|
||||
|
||||
$(DB): $(DB).tar.gz
|
||||
gunzip < $(DB).tar.gz | tar xvf -
|
||||
|
||||
have-db:
|
||||
$(MAKE) $(DB)
|
||||
touch have-db
|
||||
|
||||
build-db: have-db
|
||||
(pfx=`pwd` && \
|
||||
cd $(DB)/build_unix && \
|
||||
CC=$(CC) CXX=$(CXX) ../dist/configure --prefix=$$pfx/inst \
|
||||
--enable-cxx --disable-shared && \
|
||||
make && \
|
||||
make install)
|
||||
touch build-db
|
||||
|
||||
|
||||
# CWI ATerm
|
||||
|
||||
ATERM = aterm-2.0
|
||||
ATERM_URL = http://www.cwi.nl/projects/MetaEnv/aterm/aterm-2.0.tar.gz
|
||||
|
||||
$(ATERM).tar.gz:
|
||||
wget $(ATERM_URL)
|
||||
|
||||
$(ATERM): $(ATERM).tar.gz
|
||||
gunzip < $(ATERM).tar.gz | tar xvf -
|
||||
|
||||
have-aterm:
|
||||
$(MAKE) $(ATERM)
|
||||
touch have-aterm
|
||||
|
||||
build-aterm: have-aterm
|
||||
(pfx=`pwd` && \
|
||||
cd $(ATERM) && \
|
||||
./configure --prefix=$$pfx/inst && \
|
||||
make && \
|
||||
make install)
|
||||
touch build-aterm
|
||||
|
||||
|
||||
all: build-db build-aterm
|
||||
|
||||
EXTRA_DIST = $(DB).tar.gz $(ATERM).tar.gz
|
||||
@@ -1,9 +1,18 @@
|
||||
bin_SCRIPTS = nix-switch nix-collect-garbage \
|
||||
nix-pull-prebuilts nix-push-prebuilts
|
||||
nix-pull nix-push
|
||||
|
||||
noinst_SCRIPTS = nix-profile.sh
|
||||
|
||||
install-exec-local:
|
||||
$(INSTALL) -d $(sysconfdir)/profile.d
|
||||
$(INSTALL_PROGRAM) nix-profile.sh $(sysconfdir)/profile.d/nix.sh
|
||||
$(INSTALL) -d $(sysconfdir)/nix
|
||||
# !!! don't overwrite local modifications
|
||||
$(INSTALL_PROGRAM) prebuilts.conf $(sysconfdir)/nix/prebuilts.conf
|
||||
# !!! don't overwrite local modifications
|
||||
$(INSTALL_DATA) prebuilts.conf $(sysconfdir)/nix/prebuilts.conf
|
||||
|
||||
include ../substitute.mk
|
||||
|
||||
EXTRA_DIST = nix-switch.in nix-collect-garbage.in \
|
||||
nix-pull.in nix-push.in nix-profile.sh.in \
|
||||
prebuilts.conf
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
my $prefix = $ENV{"NIX"} || "/nix"; # !!! use prefix
|
||||
my $linkdir = "$prefix/var/nix/links";
|
||||
|
||||
my %alive;
|
||||
|
||||
open HASHES, "nix closure \$(cat $linkdir/*.hash) |";
|
||||
while (<HASHES>) {
|
||||
chomp;
|
||||
$alive{$_} = 1;
|
||||
}
|
||||
close HASHES;
|
||||
|
||||
open HASHES, "nix listinst |";
|
||||
while (<HASHES>) {
|
||||
chomp;
|
||||
if (!$alive{$_}) {
|
||||
print "$_\n";
|
||||
}
|
||||
}
|
||||
close HASHES;
|
||||
39
scripts/nix-collect-garbage.in
Executable file
39
scripts/nix-collect-garbage.in
Executable file
@@ -0,0 +1,39 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
my $linkdir = "@localstatedir@/nix/links";
|
||||
my $storedir = "@prefix@/store";
|
||||
|
||||
my %alive;
|
||||
|
||||
my $keepsuccessors = 0;
|
||||
my $invert = 0;
|
||||
|
||||
foreach my $arg (@ARGV) {
|
||||
if ($arg eq "--keep-successors") { $keepsuccessors = 1; }
|
||||
elsif ($arg eq "--invert") { $invert = 1; }
|
||||
else { die "unknown argument `$arg'" };
|
||||
}
|
||||
|
||||
my $extraarg = "";
|
||||
if ($keepsuccessors) { $extraarg = "--include-successors"; };
|
||||
open HASHES, "nix --query --requisites $extraarg \$(cat $linkdir/*.id) |" or die "in `nix -qrh'";
|
||||
while (<HASHES>) {
|
||||
chomp;
|
||||
$alive{$_} = 1;
|
||||
if ($invert) { print "$_\n"; };
|
||||
}
|
||||
close HASHES;
|
||||
|
||||
exit 0 if ($invert);
|
||||
|
||||
opendir(DIR, $storedir) or die "cannot opendir $storedir: $!";
|
||||
my @names = readdir(DIR);
|
||||
closedir DIR;
|
||||
|
||||
foreach my $name (@names) {
|
||||
next if ($name eq "." || $name eq "..");
|
||||
$name = "$storedir/$name";
|
||||
if (!$alive{$name}) {
|
||||
print "$name\n";
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
if test -z "$NIX_SET"; then
|
||||
|
||||
export NIX_SET=1
|
||||
|
||||
NIX_LINKS=/nix/var/nix/links/current
|
||||
|
||||
export PATH=$NIX_LINKS/bin:/nix/bin:$PATH
|
||||
|
||||
export LD_LIBRARY_PATH=$NIX_LINKS/lib:$LD_LIBRARY_PATH
|
||||
|
||||
export LIBRARY_PATH=$NIX_LINKS/lib:$LIBRARY_PATH
|
||||
|
||||
export C_INCLUDE_PATH=$NIX_LINKS/include:$C_INCLUDE_PATH
|
||||
|
||||
export PKG_CONFIG_PATH=$NIX_LINKS/lib/pkgconfig:$PKG_CONFIG_PATH
|
||||
|
||||
# export MANPATH=$NIX_LINKS/man:$MANPATH
|
||||
|
||||
fi
|
||||
20
scripts/nix-profile.sh.in
Normal file
20
scripts/nix-profile.sh.in
Normal file
@@ -0,0 +1,20 @@
|
||||
#if test -z "$NIX_SET"; then
|
||||
|
||||
# export NIX_SET=1
|
||||
|
||||
NIX_LINKS=@localstatedir@/nix/links/current
|
||||
|
||||
export PATH=$NIX_LINKS/bin:@prefix@/bin:$PATH
|
||||
|
||||
# export LD_LIBRARY_PATH=$NIX_LINKS/lib:$LD_LIBRARY_PATH
|
||||
|
||||
export LIBRARY_PATH=$NIX_LINKS/lib:$LIBRARY_PATH
|
||||
|
||||
export C_INCLUDE_PATH=$NIX_LINKS/include:$C_INCLUDE_PATH
|
||||
export CPLUS_INCLUDE_PATH=$NIX_LINKS/include:$CPLUS_INCLUDE_PATH
|
||||
|
||||
export PKG_CONFIG_PATH=$NIX_LINKS/lib/pkgconfig:$PKG_CONFIG_PATH
|
||||
|
||||
# export MANPATH=$NIX_LINKS/man:$MANPATH
|
||||
|
||||
#fi
|
||||
@@ -1,83 +0,0 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
my $prefix = $ENV{"NIX"} || "/nix"; # !!! use prefix
|
||||
my $etcdir = "$prefix/etc/nix";
|
||||
my $knowns = "$prefix/var/nix/known-prebuilts";
|
||||
my $tmpfile = "$prefix/var/nix/prebuilts.tmp";
|
||||
|
||||
my $conffile = "$etcdir/prebuilts.conf";
|
||||
|
||||
umask 0022;
|
||||
|
||||
sub register {
|
||||
my $fn = shift;
|
||||
my $url = shift;
|
||||
return unless $fn =~ /([^\/]*)-([0-9a-z]{32})-([0-9a-z]{32})\.tar\.bz2/;
|
||||
my $id = $1;
|
||||
my $pkghash = $2;
|
||||
my $prebuilthash = $3;
|
||||
|
||||
print "$pkghash => $prebuilthash ($id)\n";
|
||||
|
||||
system "nix regprebuilt $pkghash $prebuilthash";
|
||||
if ($?) { die "`nix regprebuilt' failed"; }
|
||||
|
||||
if ($url =~ /^\//) {
|
||||
system "nix regfile $url";
|
||||
if ($?) { die "`nix regfile' failed"; }
|
||||
} else {
|
||||
system "nix regurl $prebuilthash $url";
|
||||
if ($?) { die "`nix regurl' failed"; }
|
||||
}
|
||||
|
||||
print KNOWNS "$pkghash\n";
|
||||
}
|
||||
|
||||
open KNOWNS, ">$knowns";
|
||||
|
||||
open CONFFILE, "<$conffile";
|
||||
|
||||
while (<CONFFILE>) {
|
||||
chomp;
|
||||
if (/^\s*(\S+)\s*(\#.*)?$/) {
|
||||
my $url = $1;
|
||||
|
||||
print "obtaining prebuilt list from $url...\n";
|
||||
|
||||
if ($url =~ /^\//) {
|
||||
|
||||
# It's a local path.
|
||||
|
||||
foreach my $fn (glob "$url/*") {
|
||||
register($fn, $fn);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
# It's a URL.
|
||||
|
||||
system "wget '$url' -O '$tmpfile' 2> /dev/null"; # !!! escape
|
||||
if ($?) { die "`wget' failed"; }
|
||||
|
||||
open INDEX, "<$tmpfile";
|
||||
|
||||
while (<INDEX>) {
|
||||
# Get all links to prebuilts, that is, file names of the
|
||||
# form foo-HASH-HASH.tar.bz2.
|
||||
next unless (/HREF=\"([^\"]*)\"/);
|
||||
my $fn = $1;
|
||||
next if $fn =~ /\.\./;
|
||||
next if $fn =~ /\//;
|
||||
register($fn, "$url/$fn");
|
||||
}
|
||||
|
||||
close INDEX;
|
||||
|
||||
unlink $tmpfile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close CONFFILE;
|
||||
|
||||
close KNOWNS;
|
||||
91
scripts/nix-pull.in
Normal file
91
scripts/nix-pull.in
Normal file
@@ -0,0 +1,91 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
my $tmpfile = "@localstatedir@/nix/pull.tmp";
|
||||
my $conffile = "@sysconfdir@/nix/prebuilts.conf";
|
||||
|
||||
my @subs;
|
||||
my @sucs;
|
||||
|
||||
open CONFFILE, "<$conffile";
|
||||
|
||||
while (<CONFFILE>) {
|
||||
|
||||
chomp;
|
||||
if (/^\s*(\S+)\s*(\#.*)?$/) {
|
||||
my $url = $1;
|
||||
|
||||
print "obtaining list of Nix archives at $url...\n";
|
||||
|
||||
system "wget '$url' -O '$tmpfile' 2> /dev/null"; # !!! escape
|
||||
if ($?) { die "`wget' failed"; }
|
||||
|
||||
open INDEX, "<$tmpfile";
|
||||
|
||||
while (<INDEX>) {
|
||||
# Get all links to prebuilts, that is, file names of the
|
||||
# form foo-HASH-HASH.tar.bz2.
|
||||
next unless (/HREF=\"([^\"]*)\"/);
|
||||
my $fn = $1;
|
||||
next if $fn =~ /\.\./;
|
||||
next if $fn =~ /\//;
|
||||
next unless $fn =~ /^([0-9a-z]{32})-([0-9a-z]{32})(.*)\.nar\.bz2$/;
|
||||
my $hash = $1;
|
||||
my $id = $2;
|
||||
my $outname = $3;
|
||||
my $fsid;
|
||||
if ($outname =~ /^-/) {
|
||||
next unless $outname =~ /^-((s-([0-9a-z]{32}))?.*)$/;
|
||||
$outname = $1;
|
||||
$fsid = $3;
|
||||
} else {
|
||||
$outname = "unnamed";
|
||||
}
|
||||
|
||||
print "registering $id -> $url/$fn\n";
|
||||
|
||||
# Construct a Fix expression that fetches and unpacks a
|
||||
# Nix archive from the network.
|
||||
my $fetch =
|
||||
"App(IncludeFix(\"fetchurl/fetchurl.fix\"), " .
|
||||
"[(\"url\", \"$url/$fn\"), (\"md5\", \"$hash\")])";
|
||||
my $fixexpr =
|
||||
"App(IncludeFix(\"nar/unnar.fix\"), " .
|
||||
"[ (\"nar\", $fetch)" .
|
||||
", (\"name\", \"$outname\")" .
|
||||
", (\"id\", \"$id\")" .
|
||||
"])";
|
||||
|
||||
my $fixfile = "/tmp/nix-pull-tmp.fix";
|
||||
open FIX, ">$fixfile";
|
||||
print FIX $fixexpr;
|
||||
close FIX;
|
||||
|
||||
# Instantiate a Nix expression from the Fix expression.
|
||||
my $nid = `fix $fixfile`;
|
||||
$? and die "instantiating Nix archive expression";
|
||||
chomp $nid;
|
||||
die unless $nid =~ /^([0-9a-z]{32})$/;
|
||||
|
||||
push @subs, $id;
|
||||
push @subs, $nid;
|
||||
|
||||
# Does the name encode a successor relation?
|
||||
if (defined $fsid) {
|
||||
print "NORMAL $fsid -> $id\n";
|
||||
push @sucs, $fsid;
|
||||
push @sucs, $id;
|
||||
}
|
||||
}
|
||||
|
||||
close INDEX;
|
||||
|
||||
unlink $tmpfile;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
system "nix --substitute @subs";
|
||||
if ($?) { die "`nix --substitute' failed"; }
|
||||
|
||||
system "nix --successor @sucs";
|
||||
if ($?) { die "`nix --successor' failed"; }
|
||||
@@ -1,44 +0,0 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
my $prefix = $ENV{"NIX"} || "/nix"; # !!! use prefix
|
||||
my $etcdir = "$prefix/etc/nix";
|
||||
my $exportdir = "$prefix/var/nix/prebuilts/exports";
|
||||
my $knowns = "$prefix/var/nix/known-prebuilts";
|
||||
|
||||
umask 0022;
|
||||
|
||||
# For performance, put the known hashes in an associative array.
|
||||
my %knowns = ();
|
||||
open KNOWNS, "<$knowns";
|
||||
while (<KNOWNS>) {
|
||||
next unless /([0-9a-z]{32})/;
|
||||
$knowns{$1} = 1;
|
||||
}
|
||||
close KNOWNS;
|
||||
|
||||
# For each installed package, check whether a prebuilt is known.
|
||||
|
||||
open PKGS, "nix listinst|";
|
||||
|
||||
while (<PKGS>) {
|
||||
chomp;
|
||||
next unless /([0-9a-z]{32})/;
|
||||
my $pkghash = $1;
|
||||
if (!defined $knowns{$1}) {
|
||||
# No known prebuilt exists for this package; so export it.
|
||||
print "exporting $pkghash...\n";
|
||||
system "nix export '$exportdir' $pkghash";
|
||||
if ($?) { die "`nix export' failed"; }
|
||||
}
|
||||
}
|
||||
|
||||
close PKGS;
|
||||
|
||||
# Push the prebuilts to the server. !!! FIXME
|
||||
|
||||
system "rsync -av -e ssh '$exportdir'/ eelco\@losser.st-lab.cs.uu.nl:/home/eelco/public_html/nix-prebuilts/";
|
||||
|
||||
# Rerun `nix-pull-prebuilts' to rescan the prebuilt source locations.
|
||||
|
||||
print "running nix-pull-prebuilts...";
|
||||
system "nix-pull-prebuilts";
|
||||
81
scripts/nix-push.in
Normal file
81
scripts/nix-push.in
Normal file
@@ -0,0 +1,81 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
my @pushlist;
|
||||
|
||||
foreach my $id (@ARGV) {
|
||||
|
||||
die unless $id =~ /^([0-9a-z]{32})$/;
|
||||
|
||||
# Get all paths referenced by the normalisation of the given
|
||||
# fstate expression.
|
||||
system "nix --install $id";
|
||||
if ($?) { die "`nix --install' failed"; }
|
||||
|
||||
my @paths;
|
||||
|
||||
open PATHS, "nix --query --requisites --include-successors $id 2> /dev/null |" or die "nix -qr";
|
||||
while (<PATHS>) {
|
||||
chomp;
|
||||
die "bad: $_" unless /^\//;
|
||||
push @paths, $_;
|
||||
}
|
||||
close PATHS;
|
||||
|
||||
# Also add all normal forms that are contained in these paths.
|
||||
# open PATHS, "nix --query --generators --path @paths |" or die "nix -qg";
|
||||
# while (<PATHS>) {
|
||||
# chomp;
|
||||
# die "bad: $_" unless /^\//;
|
||||
# push @paths, $_;
|
||||
# }
|
||||
# close PATHS;
|
||||
|
||||
# For each path, create a Fix expression that turns the path into
|
||||
# a Nix archive.
|
||||
foreach my $path (@paths) {
|
||||
|
||||
next unless ($path =~ /\/([0-9a-z]{32})[^\/]*/);
|
||||
my $pathid = $1;
|
||||
|
||||
# Construct a name for the Nix archive. If the file is an
|
||||
# fstate successor, encode this into the name.
|
||||
my $name = $pathid;
|
||||
if ($path =~ /-s-([0-9a-z]{32}).nix$/) {
|
||||
$name = "$name-s-$1";
|
||||
}
|
||||
$name = $name . ".nar.bz2";
|
||||
|
||||
# Construct a Fix expression that creates a Nix archive.
|
||||
my $fixexpr =
|
||||
"App(IncludeFix(\"nar/nar.fix\"), " .
|
||||
"[ (\"path\", Slice([\"$pathid\"], [(\"$path\", \"$pathid\", [])]))" .
|
||||
"])";
|
||||
|
||||
my $fixfile = "/tmp/nix-push-tmp.fix";
|
||||
open FIX, ">$fixfile";
|
||||
print FIX $fixexpr;
|
||||
close FIX;
|
||||
|
||||
# Instantiate a Nix expression from the Fix expression.
|
||||
my $nid = `fix $fixfile`;
|
||||
$? and die "instantiating Nix archive expression";
|
||||
chomp $nid;
|
||||
die unless $nid =~ /^([0-9a-z]{32})$/;
|
||||
|
||||
# Realise the Nix expression.
|
||||
system "nix --install $nid";
|
||||
if ($?) { die "`nix --install' failed"; }
|
||||
my $npath = `nix --query --list $nid 2> /dev/null`;
|
||||
$? and die "`nix --query --list' failed";
|
||||
chomp $npath;
|
||||
|
||||
push @pushlist, "$npath/*";
|
||||
|
||||
print "$path -> $npath\n";
|
||||
}
|
||||
}
|
||||
|
||||
# Push the prebuilts to the server. !!! FIXME
|
||||
if (scalar @pushlist > 0) {
|
||||
system "rsync -av -e ssh @pushlist eelco\@losser.st-lab.cs.uu.nl:/home/eelco/public_html/nix-dist/";
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
|
||||
my $keep = 0;
|
||||
|
||||
if (scalar @ARGV > 0 && $ARGV[0] eq "--keep") {
|
||||
shift @ARGV;
|
||||
$keep = 1;
|
||||
}
|
||||
|
||||
my $hash = $ARGV[0];
|
||||
$hash || die "no package hash specified";
|
||||
|
||||
my $prefix = $ENV{"NIX"} || "/nix"; # !!! use prefix
|
||||
my $linkdir = "$prefix/var/nix/links";
|
||||
|
||||
# Build the specified package, and all its dependencies.
|
||||
my $pkgdir = `nix getpkg $hash`;
|
||||
if ($?) { die "`nix getpkg' failed"; }
|
||||
chomp $pkgdir;
|
||||
|
||||
my $id = `nix info $hash | cut -c 34-`;
|
||||
if ($?) { die "`nix info' failed"; }
|
||||
chomp $id;
|
||||
|
||||
# Figure out a generation number.
|
||||
my $nr = 0;
|
||||
while (-e "$linkdir/$id-$nr") { $nr++; }
|
||||
my $link = "$linkdir/$id-$nr";
|
||||
|
||||
# Create a symlink from $link to $pkgdir.
|
||||
symlink($pkgdir, $link) or die "cannot create $link: $!";
|
||||
|
||||
# Also store the hash of $pkgdir. This is useful for garbage
|
||||
# collection and the like.
|
||||
my $hashfile = "$linkdir/$id-$nr.hash";
|
||||
open HASH, "> $hashfile" or die "cannot create $hashfile";
|
||||
print HASH "$hash\n";
|
||||
close HASH;
|
||||
|
||||
my $current = "$linkdir/current";
|
||||
|
||||
# Read the current generation so that we can delete it (if --keep
|
||||
# wasn't specified).
|
||||
my $oldlink = readlink($current);
|
||||
|
||||
# Make $link the current generation by pointing $linkdir/current to
|
||||
# it. The rename() system call is supposed to be essentially atomic
|
||||
# on Unix. That is, if we have links `current -> X' and `new_current
|
||||
# -> Y', and we rename new_current to current, a process accessing
|
||||
# current will see X or Y, but never a file-not-found or other error
|
||||
# condition. This is sufficient to atomically switch the current link
|
||||
# tree.
|
||||
|
||||
print "switching $current to $link\n";
|
||||
|
||||
my $tmplink = "$linkdir/new_current";
|
||||
symlink($link, $tmplink) or die "cannot create $tmplink";
|
||||
rename($tmplink, $current) or die "cannot rename $tmplink";
|
||||
|
||||
if (!$keep && defined $oldlink) {
|
||||
print "deleting old $oldlink\n";
|
||||
unlink($oldlink) == 1 || print "cannot delete $oldlink\n";
|
||||
unlink("$oldlink.hash") == 1 || print "cannot delete $oldlink.hash\n";
|
||||
}
|
||||
82
scripts/nix-switch.in
Executable file
82
scripts/nix-switch.in
Executable file
@@ -0,0 +1,82 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
|
||||
my $keep = 0;
|
||||
my $sourceroot = 0;
|
||||
my $srcid;
|
||||
|
||||
foreach my $arg (@ARGV) {
|
||||
if ($arg eq "--keep") { $keep = 1; }
|
||||
elsif ($arg eq "--source-root") { $sourceroot = 1; }
|
||||
elsif ($arg =~ /^([0-9a-z]{32})$/) { $srcid = $arg; }
|
||||
else { die "unknown argument `$arg'" };
|
||||
}
|
||||
|
||||
my $linkdir = "@localstatedir@/nix/links";
|
||||
|
||||
# Build the specified package, and all its dependencies.
|
||||
my $nfid = `nix --install $srcid`;
|
||||
if ($?) { die "`nix --install' failed"; }
|
||||
chomp $nfid;
|
||||
die unless $nfid =~ /^([0-9a-z]{32})$/;
|
||||
|
||||
my $pkgdir = `nix --query --list $nfid`;
|
||||
if ($?) { die "`nix --query --list' failed"; }
|
||||
chomp $pkgdir;
|
||||
|
||||
# Figure out a generation number.
|
||||
opendir(DIR, $linkdir);
|
||||
my $nr = 0;
|
||||
foreach my $n (sort(readdir(DIR))) {
|
||||
next if (!($n =~ /^\d+$/));
|
||||
$nr = $n + 1 if ($n >= $nr);
|
||||
}
|
||||
closedir(DIR);
|
||||
|
||||
my $link = "$linkdir/$nr";
|
||||
|
||||
# Create a symlink from $link to $pkgdir.
|
||||
symlink($pkgdir, $link) or die "cannot create $link: $!";
|
||||
|
||||
# Store the id of the normal form. This is useful for garbage
|
||||
# collection and the like.
|
||||
my $idfile = "$linkdir/$nr.id";
|
||||
open ID, "> $idfile" or die "cannot create $idfile";
|
||||
print ID "$nfid\n";
|
||||
close ID;
|
||||
|
||||
# Optionally store the source id.
|
||||
if ($sourceroot) {
|
||||
$idfile = "$linkdir/$nr-src.id";
|
||||
open ID, "> $idfile" or die "cannot create $idfile";
|
||||
print ID "$srcid\n";
|
||||
close ID;
|
||||
}
|
||||
|
||||
my $current = "$linkdir/current";
|
||||
|
||||
# Read the current generation so that we can delete it (if --keep
|
||||
# wasn't specified).
|
||||
my $oldlink = readlink($current);
|
||||
|
||||
# Make $link the current generation by pointing $linkdir/current to
|
||||
# it. The rename() system call is supposed to be essentially atomic
|
||||
# on Unix. That is, if we have links `current -> X' and `new_current
|
||||
# -> Y', and we rename new_current to current, a process accessing
|
||||
# current will see X or Y, but never a file-not-found or other error
|
||||
# condition. This is sufficient to atomically switch the current link
|
||||
# tree.
|
||||
|
||||
print "switching $current to $link\n";
|
||||
|
||||
my $tmplink = "$linkdir/new_current";
|
||||
symlink($link, $tmplink) or die "cannot create $tmplink";
|
||||
rename($tmplink, $current) or die "cannot rename $tmplink";
|
||||
|
||||
if (!$keep && defined $oldlink) {
|
||||
print "deleting old $oldlink\n";
|
||||
unlink($oldlink) == 1 or print "cannot delete $oldlink\n";
|
||||
unlink("$oldlink.id") == 1 or print "cannot delete $oldlink.id\n";
|
||||
unlink("$oldlink-src.id");
|
||||
}
|
||||
@@ -1,4 +1,2 @@
|
||||
# A list of URLs or local paths from where we obtain prebuilts.
|
||||
/nix/var/nix/prebuilts/imports
|
||||
/nix/var/nix/prebuilts/exports
|
||||
http://losser.st-lab.cs.uu.nl/~eelco/nix-prebuilts/
|
||||
# A list of URLs from where we obtain Nix archives.
|
||||
http://losser.st-lab.cs.uu.nl/~eelco/nix-dist/
|
||||
|
||||
@@ -1,24 +1,51 @@
|
||||
bin_PROGRAMS = nix fix
|
||||
noinst_PROGRAMS = test
|
||||
bin_PROGRAMS = nix nix-hash fix
|
||||
check_PROGRAMS = test
|
||||
|
||||
AM_CXXFLAGS = -DSYSTEM=\"@host@\" -Wall
|
||||
AM_CXXFLAGS = -DSYSTEM=\"@host@\" -Wall -I.. -I../externals/inst/include $(CXXFLAGS)
|
||||
AM_LDFLAGS = -L../externals/inst/lib -ldb_cxx -lATerm $(LDFLAGS)
|
||||
|
||||
nix_SOURCES = nix.cc db.cc util.cc hash.cc md5.c
|
||||
nix_LDADD = -ldb_cxx-4 -lATerm
|
||||
nix_SOURCES = nix.cc
|
||||
nix_LDADD = libshared.a libnix.a -ldb_cxx -lATerm
|
||||
|
||||
fix_SOURCES = fix.cc util.cc hash.cc md5.c
|
||||
fix_LDADD = -lATerm
|
||||
nix_hash_SOURCES = nix-hash.cc
|
||||
nix_hash_LDADD = libshared.a libnix.a -ldb_cxx -lATerm
|
||||
|
||||
test_SOURCES = test.cc util.cc hash.cc md5.c
|
||||
fix_SOURCES = fix.cc
|
||||
fix_LDADD = libshared.a libnix.a -ldb_cxx -lATerm
|
||||
|
||||
TESTS = test
|
||||
|
||||
test_SOURCES = test.cc
|
||||
test_LDADD = libshared.a libnix.a -ldb_cxx -lATerm
|
||||
|
||||
noinst_LIBRARIES = libnix.a libshared.a
|
||||
|
||||
libnix_a_SOURCES = util.cc hash.cc archive.cc md5.c \
|
||||
store.cc fstate.cc normalise.cc exec.cc \
|
||||
globals.cc db.cc references.cc
|
||||
|
||||
libshared_a_SOURCES = shared.cc
|
||||
|
||||
libshared_a_CXXFLAGS = \
|
||||
-DNIX_STORE_DIR=\"$(prefix)/store\" \
|
||||
-DNIX_DATA_DIR=\"$(datadir)\" \
|
||||
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
|
||||
-DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
|
||||
$(AM_CXXFLAGS)
|
||||
|
||||
nix.o: nix-help.txt.hh
|
||||
|
||||
%.hh: %
|
||||
echo -n '"' > $@
|
||||
sed 's|\(.*\)|\1\\n\\|' < $< >> $@
|
||||
echo '"' >> $@
|
||||
|
||||
install-data-local:
|
||||
$(INSTALL) -d $(localstatedir)/nix
|
||||
$(INSTALL) -d $(localstatedir)/nix/descriptors
|
||||
$(INSTALL) -d $(localstatedir)/nix/sources
|
||||
$(INSTALL) -d $(localstatedir)/nix/links
|
||||
$(INSTALL) -d $(localstatedir)/nix/prebuilts
|
||||
$(INSTALL) -d $(localstatedir)/nix/prebuilts/imports
|
||||
$(INSTALL) -d $(localstatedir)/nix/prebuilts/exports
|
||||
ln -sf $(localstatedir)/nix/links/current $(prefix)/current
|
||||
$(INSTALL) -d $(localstatedir)/log/nix
|
||||
$(INSTALL) -d $(prefix)/pkg
|
||||
$(bindir)/nix init
|
||||
$(INSTALL) -d $(prefix)/store
|
||||
$(bindir)/nix --init
|
||||
|
||||
EXTRA_DIST = *.hh *.h
|
||||
|
||||
338
src/archive.cc
Normal file
338
src/archive.cc
Normal file
@@ -0,0 +1,338 @@
|
||||
#include <vector>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "archive.hh"
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
static string archiveVersion1 = "nix-archive-1";
|
||||
|
||||
|
||||
static void writePadding(unsigned int len, DumpSink & sink)
|
||||
{
|
||||
if (len % 8) {
|
||||
unsigned char zero[8];
|
||||
memset(zero, 0, sizeof(zero));
|
||||
sink(zero, 8 - (len % 8));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void writeInt(unsigned int n, DumpSink & sink)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
buf[0] = n & 0xff;
|
||||
buf[1] = (n >> 8) & 0xff;
|
||||
buf[2] = (n >> 16) & 0xff;
|
||||
buf[3] = (n >> 24) & 0xff;
|
||||
sink(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
|
||||
static void writeString(const string & s, DumpSink & sink)
|
||||
{
|
||||
unsigned int len = s.length();
|
||||
writeInt(len, sink);
|
||||
sink((const unsigned char *) s.c_str(), len);
|
||||
writePadding(len, sink);
|
||||
}
|
||||
|
||||
|
||||
static void dump(const string & path, DumpSink & sink);
|
||||
|
||||
|
||||
static void dumpEntries(const string & path, DumpSink & sink)
|
||||
{
|
||||
DIR * dir = opendir(path.c_str());
|
||||
if (!dir) throw SysError("opening directory " + path);
|
||||
|
||||
vector<string> names;
|
||||
|
||||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir)) {
|
||||
string name = dirent->d_name;
|
||||
if (name == "." || name == "..") continue;
|
||||
names.push_back(name);
|
||||
}
|
||||
if (errno) throw SysError("reading directory " + path);
|
||||
|
||||
sort(names.begin(), names.end());
|
||||
|
||||
for (vector<string>::iterator it = names.begin();
|
||||
it != names.end(); it++)
|
||||
{
|
||||
writeString("entry", sink);
|
||||
writeString("(", sink);
|
||||
writeString("name", sink);
|
||||
writeString(*it, sink);
|
||||
writeString("node", sink);
|
||||
dump(path + "/" + *it, sink);
|
||||
writeString(")", sink);
|
||||
}
|
||||
|
||||
closedir(dir); /* !!! close on exception */
|
||||
}
|
||||
|
||||
|
||||
static void dumpContents(const string & path, unsigned int size,
|
||||
DumpSink & sink)
|
||||
{
|
||||
writeString("contents", sink);
|
||||
writeInt(size, sink);
|
||||
|
||||
int fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) throw SysError(format("opening file `%1%'") % path);
|
||||
|
||||
unsigned char buf[65536];
|
||||
|
||||
unsigned int total = 0;
|
||||
ssize_t n;
|
||||
while ((n = read(fd, buf, sizeof(buf)))) {
|
||||
if (n == -1) throw SysError("reading file " + path);
|
||||
total += n;
|
||||
sink(buf, n);
|
||||
}
|
||||
|
||||
if (total != size)
|
||||
throw SysError("file changed while reading it: " + path);
|
||||
|
||||
writePadding(size, sink);
|
||||
|
||||
close(fd); /* !!! close on exception */
|
||||
}
|
||||
|
||||
|
||||
static void dump(const string & path, DumpSink & sink)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
|
||||
writeString("(", sink);
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
writeString("type", sink);
|
||||
writeString("regular", sink);
|
||||
if (st.st_mode & S_IXUSR) {
|
||||
writeString("executable", sink);
|
||||
writeString("", sink);
|
||||
}
|
||||
dumpContents(path, st.st_size, sink);
|
||||
}
|
||||
|
||||
else if (S_ISDIR(st.st_mode)) {
|
||||
writeString("type", sink);
|
||||
writeString("directory", sink);
|
||||
dumpEntries(path, sink);
|
||||
}
|
||||
|
||||
else if (S_ISLNK(st.st_mode)) {
|
||||
writeString("type", sink);
|
||||
writeString("symlink", sink);
|
||||
char buf[st.st_size];
|
||||
if (readlink(path.c_str(), buf, st.st_size) != st.st_size)
|
||||
throw SysError("reading symbolic link " + path);
|
||||
writeString("target", sink);
|
||||
writeString(string(buf, st.st_size), sink);
|
||||
}
|
||||
|
||||
else throw Error("unknown file type: " + path);
|
||||
|
||||
writeString(")", sink);
|
||||
}
|
||||
|
||||
|
||||
void dumpPath(const string & path, DumpSink & sink)
|
||||
{
|
||||
writeString(archiveVersion1, sink);
|
||||
dump(path, sink);
|
||||
}
|
||||
|
||||
|
||||
static Error badArchive(string s)
|
||||
{
|
||||
return Error("bad archive: " + s);
|
||||
}
|
||||
|
||||
|
||||
static void readPadding(unsigned int len, RestoreSource & source)
|
||||
{
|
||||
if (len % 8) {
|
||||
unsigned char zero[8];
|
||||
unsigned int n = 8 - (len % 8);
|
||||
source(zero, n);
|
||||
for (unsigned int i = 0; i < n; i++)
|
||||
if (zero[i]) throw badArchive("non-zero padding");
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int readInt(RestoreSource & source)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
source(buf, sizeof(buf));
|
||||
if (buf[4] || buf[5] || buf[6] || buf[7])
|
||||
throw Error("implementation cannot deal with > 32-bit integers");
|
||||
return
|
||||
buf[0] |
|
||||
(buf[1] << 8) |
|
||||
(buf[2] << 16) |
|
||||
(buf[3] << 24);
|
||||
}
|
||||
|
||||
|
||||
static string readString(RestoreSource & source)
|
||||
{
|
||||
unsigned int len = readInt(source);
|
||||
char buf[len];
|
||||
source((unsigned char *) buf, len);
|
||||
readPadding(len, source);
|
||||
return string(buf, len);
|
||||
}
|
||||
|
||||
|
||||
static void skipGeneric(RestoreSource & source)
|
||||
{
|
||||
if (readString(source) == "(") {
|
||||
while (readString(source) != ")")
|
||||
skipGeneric(source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void restore(const string & path, RestoreSource & source);
|
||||
|
||||
|
||||
static void restoreEntry(const string & path, RestoreSource & source)
|
||||
{
|
||||
string s, name;
|
||||
|
||||
s = readString(source);
|
||||
if (s != "(") throw badArchive("expected open tag");
|
||||
|
||||
while (1) {
|
||||
s = readString(source);
|
||||
|
||||
if (s == ")") {
|
||||
break;
|
||||
} else if (s == "name") {
|
||||
name = readString(source);
|
||||
} else if (s == "node") {
|
||||
if (s == "") throw badArchive("entry name missing");
|
||||
restore(path + "/" + name, source);
|
||||
} else {
|
||||
throw badArchive("unknown field " + s);
|
||||
skipGeneric(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void restoreContents(int fd, const string & path, RestoreSource & source)
|
||||
{
|
||||
unsigned int size = readInt(source);
|
||||
unsigned int left = size;
|
||||
unsigned char buf[65536];
|
||||
|
||||
while (left) {
|
||||
unsigned int n = sizeof(buf);
|
||||
if (n > left) n = left;
|
||||
source(buf, n);
|
||||
if (write(fd, buf, n) != (ssize_t) n)
|
||||
throw SysError("writing file " + path);
|
||||
left -= n;
|
||||
}
|
||||
|
||||
readPadding(size, source);
|
||||
}
|
||||
|
||||
|
||||
static void restore(const string & path, RestoreSource & source)
|
||||
{
|
||||
string s;
|
||||
|
||||
s = readString(source);
|
||||
if (s != "(") throw badArchive("expected open tag");
|
||||
|
||||
enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
|
||||
int fd = -1; /* !!! close on exception */
|
||||
|
||||
while (1) {
|
||||
s = readString(source);
|
||||
|
||||
if (s == ")") {
|
||||
break;
|
||||
}
|
||||
|
||||
else if (s == "type") {
|
||||
if (type != tpUnknown)
|
||||
throw badArchive("multiple type fields");
|
||||
string t = readString(source);
|
||||
|
||||
if (t == "regular") {
|
||||
type = tpRegular;
|
||||
fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
|
||||
if (fd == -1)
|
||||
throw SysError("creating file " + path);
|
||||
}
|
||||
|
||||
else if (t == "directory") {
|
||||
type = tpDirectory;
|
||||
if (mkdir(path.c_str(), 0777) == -1)
|
||||
throw SysError("creating directory " + path);
|
||||
}
|
||||
|
||||
else if (t == "symlink") {
|
||||
type = tpSymlink;
|
||||
}
|
||||
|
||||
else throw badArchive("unknown file type " + t);
|
||||
|
||||
}
|
||||
|
||||
else if (s == "contents" && type == tpRegular) {
|
||||
restoreContents(fd, path, source);
|
||||
}
|
||||
|
||||
else if (s == "executable" && type == tpRegular) {
|
||||
readString(source);
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == -1)
|
||||
throw SysError("fstat");
|
||||
if (fchmod(fd, st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
|
||||
throw SysError("fchmod");
|
||||
}
|
||||
|
||||
else if (s == "entry" && type == tpDirectory) {
|
||||
restoreEntry(path, source);
|
||||
}
|
||||
|
||||
else if (s == "target" && type == tpSymlink) {
|
||||
string target = readString(source);
|
||||
if (symlink(target.c_str(), path.c_str()) == -1)
|
||||
throw SysError("creating symlink " + path);
|
||||
}
|
||||
|
||||
else {
|
||||
throw badArchive("unknown field " + s);
|
||||
skipGeneric(source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (fd != -1) close(fd);
|
||||
}
|
||||
|
||||
|
||||
void restorePath(const string & path, RestoreSource & source)
|
||||
{
|
||||
if (readString(source) != archiveVersion1)
|
||||
throw badArchive("expected Nix archive");
|
||||
restore(path, source);
|
||||
}
|
||||
|
||||
60
src/archive.hh
Normal file
60
src/archive.hh
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
/* dumpPath creates a Nix archive of the specified path. The format
|
||||
is as follows:
|
||||
|
||||
IF path points to a REGULAR FILE:
|
||||
dump(path) = attrs(
|
||||
[ ("type", "regular")
|
||||
, ("contents", contents(path))
|
||||
])
|
||||
|
||||
IF path points to a DIRECTORY:
|
||||
dump(path) = attrs(
|
||||
[ ("type", "directory")
|
||||
, ("entries", concat(map(f, sort(entries(path)))))
|
||||
])
|
||||
where f(fn) = attrs(
|
||||
[ ("name", fn)
|
||||
, ("file", dump(path + "/" + fn))
|
||||
])
|
||||
|
||||
where:
|
||||
|
||||
attrs(as) = concat(map(attr, as)) + encN(0)
|
||||
attrs((a, b)) = encS(a) + encS(b)
|
||||
|
||||
encS(s) = encN(len(s)) + s + (padding until next 64-bit boundary)
|
||||
|
||||
encN(n) = 64-bit little-endian encoding of n.
|
||||
|
||||
contents(path) = the contents of a regular file.
|
||||
|
||||
sort(strings) = lexicographic sort by 8-bit value (strcmp).
|
||||
|
||||
entries(path) = the entries of a directory, without `.' and
|
||||
`..'.
|
||||
|
||||
`+' denotes string concatenation. */
|
||||
|
||||
struct DumpSink
|
||||
{
|
||||
virtual void operator () (const unsigned char * data, unsigned int len) = 0;
|
||||
};
|
||||
|
||||
void dumpPath(const string & path, DumpSink & sink);
|
||||
|
||||
|
||||
struct RestoreSource
|
||||
{
|
||||
/* The callee should store exactly *len bytes in the buffer
|
||||
pointed to by data. It should block if that much data is not
|
||||
yet available, or throw an error if it is not going to be
|
||||
available. */
|
||||
virtual void operator () (unsigned char * data, unsigned int len) = 0;
|
||||
};
|
||||
|
||||
void restorePath(const string & path, RestoreSource & source);
|
||||
72
src/db.cc
72
src/db.cc
@@ -65,7 +65,10 @@ bool queryDB(const string & filename, const string & dbname,
|
||||
err = db->get(0, &kt, &dt, 0);
|
||||
if (err) return false;
|
||||
|
||||
data = string((char *) dt.get_data(), dt.get_size());
|
||||
if (!dt.get_data())
|
||||
data = "";
|
||||
else
|
||||
data = string((char *) dt.get_data(), dt.get_size());
|
||||
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
|
||||
@@ -73,6 +76,40 @@ bool queryDB(const string & filename, const string & dbname,
|
||||
}
|
||||
|
||||
|
||||
bool queryListDB(const string & filename, const string & dbname,
|
||||
const string & key, Strings & data)
|
||||
{
|
||||
string d;
|
||||
|
||||
if (!queryDB(filename, dbname, key, d))
|
||||
return false;
|
||||
|
||||
string::iterator it = d.begin();
|
||||
|
||||
while (it != d.end()) {
|
||||
|
||||
if (it + 4 > d.end())
|
||||
throw Error(format("short db entry: `%1%'") % d);
|
||||
|
||||
unsigned int len;
|
||||
len = (unsigned char) *it++;
|
||||
len |= ((unsigned char) *it++) << 8;
|
||||
len |= ((unsigned char) *it++) << 16;
|
||||
len |= ((unsigned char) *it++) << 24;
|
||||
|
||||
if (it + len > d.end())
|
||||
throw Error(format("short db entry: `%1%'") % d);
|
||||
|
||||
string s;
|
||||
while (len--) s += *it++;
|
||||
|
||||
data.push_back(s);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void setDB(const string & filename, const string & dbname,
|
||||
const string & key, const string & data)
|
||||
{
|
||||
@@ -85,6 +122,29 @@ void setDB(const string & filename, const string & dbname,
|
||||
}
|
||||
|
||||
|
||||
void setListDB(const string & filename, const string & dbname,
|
||||
const string & key, const Strings & data)
|
||||
{
|
||||
string d;
|
||||
|
||||
for (Strings::const_iterator it = data.begin();
|
||||
it != data.end(); it++)
|
||||
{
|
||||
string s = *it;
|
||||
unsigned int len = s.size();
|
||||
|
||||
d += len & 0xff;
|
||||
d += (len >> 8) & 0xff;
|
||||
d += (len >> 16) & 0xff;
|
||||
d += (len >> 24) & 0xff;
|
||||
|
||||
d += s;
|
||||
}
|
||||
|
||||
setDB(filename, dbname, key, d);
|
||||
}
|
||||
|
||||
|
||||
void delDB(const string & filename, const string & dbname,
|
||||
const string & key)
|
||||
{
|
||||
@@ -97,7 +157,7 @@ void delDB(const string & filename, const string & dbname,
|
||||
|
||||
|
||||
void enumDB(const string & filename, const string & dbname,
|
||||
DBPairs & contents)
|
||||
Strings & keys)
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -108,11 +168,9 @@ void enumDB(const string & filename, const string & dbname,
|
||||
DbcClose cursorCloser(cursor);
|
||||
|
||||
Dbt kt, dt;
|
||||
while (cursor->get(&kt, &dt, DB_NEXT) != DB_NOTFOUND) {
|
||||
string key((char *) kt.get_data(), kt.get_size());
|
||||
string data((char *) dt.get_data(), dt.get_size());
|
||||
contents.push_back(DBPair(key, data));
|
||||
}
|
||||
while (cursor->get(&kt, &dt, DB_NEXT) != DB_NOTFOUND)
|
||||
keys.push_back(
|
||||
string((char *) kt.get_data(), kt.get_size()));
|
||||
|
||||
} catch (DbException e) { rethrow(e); }
|
||||
}
|
||||
|
||||
13
src/db.hh
13
src/db.hh
@@ -4,23 +4,28 @@
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
using namespace std;
|
||||
#include "util.hh"
|
||||
|
||||
typedef pair<string, string> DBPair;
|
||||
typedef list<DBPair> DBPairs;
|
||||
using namespace std;
|
||||
|
||||
void createDB(const string & filename, const string & dbname);
|
||||
|
||||
bool queryDB(const string & filename, const string & dbname,
|
||||
const string & key, string & data);
|
||||
|
||||
bool queryListDB(const string & filename, const string & dbname,
|
||||
const string & key, Strings & data);
|
||||
|
||||
void setDB(const string & filename, const string & dbname,
|
||||
const string & key, const string & data);
|
||||
|
||||
void setListDB(const string & filename, const string & dbname,
|
||||
const string & key, const Strings & data);
|
||||
|
||||
void delDB(const string & filename, const string & dbname,
|
||||
const string & key);
|
||||
|
||||
void enumDB(const string & filename, const string & dbname,
|
||||
DBPairs & contents);
|
||||
Strings & keys);
|
||||
|
||||
#endif /* !__DB_H */
|
||||
|
||||
119
src/exec.cc
Normal file
119
src/exec.cc
Normal file
@@ -0,0 +1,119 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "exec.hh"
|
||||
#include "util.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
|
||||
class AutoDelete
|
||||
{
|
||||
string path;
|
||||
bool del;
|
||||
public:
|
||||
|
||||
AutoDelete(const string & p) : path(p)
|
||||
{
|
||||
del = true;
|
||||
}
|
||||
|
||||
~AutoDelete()
|
||||
{
|
||||
if (del) deletePath(path);
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
del = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* Run a program. */
|
||||
void runProgram(const string & program, Environment env)
|
||||
{
|
||||
/* Create a log file. */
|
||||
string logFileName = nixLogDir + "/run.log";
|
||||
/* !!! auto-pclose on exit */
|
||||
FILE * logFile = popen(("tee -a " + logFileName + " >&2").c_str(), "w"); /* !!! escaping */
|
||||
if (!logFile)
|
||||
throw SysError(format("creating log file `%1%'") % logFileName);
|
||||
|
||||
/* Create a temporary directory where the build will take
|
||||
place. */
|
||||
static int counter = 0;
|
||||
string tmpDir = (format("/tmp/nix-%1%-%2%") % getpid() % counter++).str();
|
||||
|
||||
if (mkdir(tmpDir.c_str(), 0777) == -1)
|
||||
throw SysError(format("creating directory `%1%'") % tmpDir);
|
||||
|
||||
AutoDelete delTmpDir(tmpDir);
|
||||
|
||||
/* Fork a child to build the package. */
|
||||
pid_t pid;
|
||||
switch (pid = fork()) {
|
||||
|
||||
case -1:
|
||||
throw SysError("unable to fork");
|
||||
|
||||
case 0:
|
||||
|
||||
try { /* child */
|
||||
|
||||
if (chdir(tmpDir.c_str()) == -1)
|
||||
throw SysError(format("changing into to `%1%'") % tmpDir);
|
||||
|
||||
/* Fill in the environment. We don't bother freeing
|
||||
the strings, since we'll exec or die soon
|
||||
anyway. */
|
||||
const char * env2[env.size() + 1];
|
||||
int i = 0;
|
||||
for (Environment::iterator it = env.begin();
|
||||
it != env.end(); it++, i++)
|
||||
env2[i] = (new string(it->first + "=" + it->second))->c_str();
|
||||
env2[i] = 0;
|
||||
|
||||
/* Dup the log handle into stderr. */
|
||||
if (dup2(fileno(logFile), STDERR_FILENO) == -1)
|
||||
throw SysError("cannot pipe standard error into log file");
|
||||
|
||||
/* Dup stderr to stdin. */
|
||||
if (dup2(STDERR_FILENO, STDOUT_FILENO) == -1)
|
||||
throw SysError("cannot dup stderr into stdout");
|
||||
|
||||
/* Execute the program. This should not return. */
|
||||
execle(program.c_str(), baseNameOf(program).c_str(), 0, env2);
|
||||
|
||||
throw SysError(format("unable to execute %1%") % program);
|
||||
|
||||
} catch (exception & e) {
|
||||
cerr << format("build error: %1%\n") % e.what();
|
||||
}
|
||||
_exit(1);
|
||||
|
||||
}
|
||||
|
||||
/* parent */
|
||||
|
||||
/* Close the logging pipe. Note that this should not cause
|
||||
the logger to exit until builder exits (because the latter
|
||||
has an open file handle to the former). */
|
||||
pclose(logFile);
|
||||
|
||||
/* Wait for the child to finish. */
|
||||
int status;
|
||||
if (waitpid(pid, &status, 0) != pid)
|
||||
throw Error("unable to wait for child");
|
||||
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
delTmpDir.cancel();
|
||||
throw Error("unable to build package");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
src/exec.hh
Normal file
18
src/exec.hh
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef __EXEC_H
|
||||
#define __EXEC_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
/* A Unix environment is a mapping from strings to strings. */
|
||||
typedef map<string, string> Environment;
|
||||
|
||||
|
||||
/* Run a program. */
|
||||
void runProgram(const string & program, Environment env);
|
||||
|
||||
|
||||
#endif /* !__EXEC_H */
|
||||
639
src/fix.cc
639
src/fix.cc
@@ -1,368 +1,343 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
extern "C" {
|
||||
#include <aterm2.h>
|
||||
}
|
||||
|
||||
#include "util.hh"
|
||||
#include "hash.hh"
|
||||
#include "globals.hh"
|
||||
#include "normalise.hh"
|
||||
#include "shared.hh"
|
||||
|
||||
|
||||
static string nixDescriptorDir;
|
||||
typedef ATerm Expr;
|
||||
|
||||
typedef map<ATerm, ATerm> NormalForms;
|
||||
typedef map<FSId, Hash> PkgHashes;
|
||||
|
||||
static bool verbose = false;
|
||||
|
||||
|
||||
/* Mapping of Fix file names to the hashes of the resulting Nix
|
||||
descriptors. */
|
||||
typedef map<string, Hash> DescriptorMap;
|
||||
|
||||
|
||||
void registerFile(string filename)
|
||||
struct EvalState
|
||||
{
|
||||
int res = system(("nix regfile " + filename).c_str());
|
||||
/* !!! escape */
|
||||
if (WEXITSTATUS(res) != 0)
|
||||
throw Error("cannot register " + filename + " with Nix");
|
||||
}
|
||||
|
||||
|
||||
void registerURL(Hash hash, string url)
|
||||
{
|
||||
int res = system(("nix regurl " + (string) hash + " " + url).c_str());
|
||||
/* !!! escape */
|
||||
if (WEXITSTATUS(res) != 0)
|
||||
throw Error("cannot register " +
|
||||
(string) hash + " -> " + url + " with Nix");
|
||||
}
|
||||
|
||||
|
||||
Error badTerm(const string & msg, ATerm e)
|
||||
{
|
||||
char * s = ATwriteToString(e);
|
||||
return Error(msg + ", in `" + s + "'");
|
||||
}
|
||||
|
||||
|
||||
/* Term evaluation. */
|
||||
|
||||
typedef map<string, ATerm> BindingsMap;
|
||||
|
||||
struct EvalContext
|
||||
{
|
||||
string dir;
|
||||
DescriptorMap * done;
|
||||
BindingsMap * vars;
|
||||
Strings searchDirs;
|
||||
NormalForms normalForms;
|
||||
PkgHashes pkgHashes; /* normalised package hashes */
|
||||
};
|
||||
|
||||
|
||||
ATerm evaluate(ATerm e, EvalContext ctx);
|
||||
Hash instantiateDescriptor(string filename, EvalContext ctx);
|
||||
static Expr evalFile(EvalState & state, string fileName);
|
||||
static Expr evalExpr(EvalState & state, Expr e);
|
||||
|
||||
|
||||
string evaluateStr(ATerm e, EvalContext ctx)
|
||||
static string searchPath(const Strings & searchDirs, string relPath)
|
||||
{
|
||||
e = evaluate(e, ctx);
|
||||
char * s;
|
||||
if (ATmatch(e, "Str(<str>)", &s))
|
||||
return s;
|
||||
else throw badTerm("string value expected", e);
|
||||
if (string(relPath, 0, 1) == "/") return relPath;
|
||||
|
||||
for (Strings::const_iterator i = searchDirs.begin();
|
||||
i != searchDirs.end(); i++)
|
||||
{
|
||||
string path = *i + "/" + relPath;
|
||||
if (pathExists(path)) return path;
|
||||
}
|
||||
|
||||
throw Error(
|
||||
format("path `%1%' not found in any of the search directories")
|
||||
% relPath);
|
||||
}
|
||||
|
||||
|
||||
bool evaluateBool(ATerm e, EvalContext ctx)
|
||||
{
|
||||
e = evaluate(e, ctx);
|
||||
if (ATmatch(e, "Bool(True)"))
|
||||
return true;
|
||||
else if (ATmatch(e, "Bool(False)"))
|
||||
return false;
|
||||
else throw badTerm("boolean value expected", e);
|
||||
}
|
||||
|
||||
|
||||
ATerm evaluate(ATerm e, EvalContext ctx)
|
||||
static Expr substExpr(string x, Expr rep, Expr e)
|
||||
{
|
||||
char * s;
|
||||
ATerm e2, e3;
|
||||
ATerm eCond, eTrue, eFalse;
|
||||
Expr e2;
|
||||
|
||||
/* Check for normal forms first. */
|
||||
|
||||
if (ATmatch(e, "Str(<str>)", &s) ||
|
||||
ATmatch(e, "Bool(True)") || ATmatch(e, "Bool(False)"))
|
||||
return e;
|
||||
|
||||
else if (
|
||||
ATmatch(e, "Pkg(<str>)", &s) ||
|
||||
ATmatch(e, "File(<str>)", &s))
|
||||
{
|
||||
parseHash(s);
|
||||
return e;
|
||||
}
|
||||
|
||||
/* Short-hands. */
|
||||
|
||||
else if (ATmatch(e, "<str>", &s))
|
||||
return ATmake("Str(<str>)", s);
|
||||
|
||||
else if (ATmatch(e, "True", &s))
|
||||
return ATmake("Bool(True)", s);
|
||||
|
||||
else if (ATmatch(e, "False", &s))
|
||||
return ATmake("Bool(False)", s);
|
||||
|
||||
/* Functions. */
|
||||
|
||||
/* `Var' looks up a variable. */
|
||||
else if (ATmatch(e, "Var(<str>)", &s)) {
|
||||
string name(s);
|
||||
ATerm e2 = (*ctx.vars)[name];
|
||||
if (!e2) throw Error("undefined variable " + name);
|
||||
return evaluate(e2, ctx); /* !!! update binding */
|
||||
}
|
||||
|
||||
/* `Fix' recursively instantiates a Fix descriptor, returning the
|
||||
hash of the generated Nix descriptor. */
|
||||
else if (ATmatch(e, "Fix(<term>)", &e2)) {
|
||||
string filename = absPath(evaluateStr(e2, ctx), ctx.dir); /* !!! */
|
||||
return ATmake("Pkg(<str>)",
|
||||
((string) instantiateDescriptor(filename, ctx)).c_str());
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* `Source' copies the specified file to nixSourcesDir, registers
|
||||
it with Nix, and returns the hash of the file. */
|
||||
else if (ATmatch(e, "Source(<term>)", &e2)) {
|
||||
string source = absPath(evaluateStr(e2, ctx), ctx.dir); /* !!! */
|
||||
string target = nixSourcesDir + "/" + baseNameOf(source);
|
||||
|
||||
// Don't copy if filename is already in nixSourcesDir.
|
||||
if (source != target) {
|
||||
if (verbose)
|
||||
cerr << "copying source " << source << endl;
|
||||
string cmd = "cp -p " + source + " " + target;
|
||||
int res = system(cmd.c_str());
|
||||
if (WEXITSTATUS(res) != 0)
|
||||
throw Error("cannot copy " + source + " to " + target);
|
||||
}
|
||||
|
||||
registerFile(target);
|
||||
return ATmake("File(<str>)", hashFile(target).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
/* `Local' registers a file with Nix, and returns the file's
|
||||
hash. */
|
||||
else if (ATmatch(e, "Local(<term>)", &e2)) {
|
||||
string filename = absPath(evaluateStr(e2, ctx), ctx.dir); /* !!! */
|
||||
Hash hash = hashFile(filename);
|
||||
registerFile(filename); /* !!! */
|
||||
return ATmake("File(<str>)", ((string) hash).c_str());
|
||||
}
|
||||
|
||||
/* `Url' registers a mapping from a hash to an url with Nix, and
|
||||
returns the hash. */
|
||||
else if (ATmatch(e, "Url(<term>, <term>)", &e2, &e3)) {
|
||||
Hash hash = parseHash(evaluateStr(e2, ctx));
|
||||
string url = evaluateStr(e3, ctx);
|
||||
registerURL(hash, url);
|
||||
return ATmake("File(<str>)", ((string) hash).c_str());
|
||||
}
|
||||
|
||||
/* `If' provides conditional evaluation. */
|
||||
else if (ATmatch(e, "If(<term>, <term>, <term>)",
|
||||
&eCond, &eTrue, &eFalse))
|
||||
return evaluate(evaluateBool(eCond, ctx) ? eTrue : eFalse, ctx);
|
||||
|
||||
else throw badTerm("invalid expression", e);
|
||||
}
|
||||
|
||||
|
||||
string getStringFromMap(BindingsMap & bindingsMap,
|
||||
const string & name)
|
||||
{
|
||||
ATerm e = bindingsMap[name];
|
||||
if (!e) throw Error("binding " + name + " is not set");
|
||||
char * s;
|
||||
if (ATmatch(e, "Str(<str>)", &s))
|
||||
return s;
|
||||
else
|
||||
throw Error("binding " + name + " is not a string");
|
||||
}
|
||||
|
||||
|
||||
/* Instantiate a Fix descriptors into a Nix descriptor, recursively
|
||||
instantiating referenced descriptors as well. */
|
||||
Hash instantiateDescriptor(string filename, EvalContext ctx)
|
||||
{
|
||||
/* Already done? */
|
||||
DescriptorMap::iterator isInMap = ctx.done->find(filename);
|
||||
if (isInMap != ctx.done->end()) return isInMap->second;
|
||||
|
||||
/* No. */
|
||||
ctx.dir = dirOf(filename);
|
||||
|
||||
/* Read the Fix descriptor as an ATerm. */
|
||||
ATerm inTerm = ATreadFromNamedFile(filename.c_str());
|
||||
if (!inTerm) throw Error("cannot read aterm " + filename);
|
||||
|
||||
ATerm bindings;
|
||||
if (!ATmatch(inTerm, "Descr(<term>)", &bindings))
|
||||
throw Error("invalid term in " + filename);
|
||||
|
||||
/* Iterate over the bindings and evaluate them to normal form. */
|
||||
BindingsMap bindingsMap; /* the normal forms */
|
||||
ctx.vars = &bindingsMap;
|
||||
|
||||
char * cname;
|
||||
ATerm value;
|
||||
while (ATmatch(bindings, "[Bind(<str>, <term>), <list>]",
|
||||
&cname, &value, &bindings))
|
||||
{
|
||||
string name(cname);
|
||||
ATerm e = evaluate(value, ctx);
|
||||
bindingsMap[name] = e;
|
||||
}
|
||||
|
||||
/* Construct a descriptor identifier by concatenating the package
|
||||
and release ids. */
|
||||
string pkgId = getStringFromMap(bindingsMap, "pkgId");
|
||||
string releaseId = getStringFromMap(bindingsMap, "releaseId");
|
||||
string id = pkgId + "-" + releaseId;
|
||||
bindingsMap["id"] = ATmake("Str(<str>)", id.c_str());
|
||||
|
||||
/* Add a system name. */
|
||||
bindingsMap["system"] = ATmake("Str(<str>)", thisSystem.c_str());
|
||||
|
||||
/* Construct the resulting ATerm. Note that iterating over the
|
||||
map yields the bindings in sorted order, which is exactly the
|
||||
canonical form for Nix descriptors. */
|
||||
ATermList bindingsList = ATempty;
|
||||
for (BindingsMap::iterator it = bindingsMap.begin();
|
||||
it != bindingsMap.end(); it++)
|
||||
/* !!! O(n^2) */
|
||||
bindingsList = ATappend(bindingsList,
|
||||
ATmake("Bind(<str>, <term>)", it->first.c_str(), it->second));
|
||||
ATerm outTerm = ATmake("Descr(<term>)", bindingsList);
|
||||
|
||||
/* Write out the resulting ATerm. */
|
||||
string tmpFilename = nixDescriptorDir + "/tmp";
|
||||
if (!ATwriteToNamedTextFile(outTerm, tmpFilename.c_str()))
|
||||
throw Error("cannot write aterm to " + tmpFilename);
|
||||
|
||||
Hash outHash = hashFile(tmpFilename);
|
||||
string outFilename = nixDescriptorDir + "/" +
|
||||
id + "-" + (string) outHash + ".nix";
|
||||
if (rename(tmpFilename.c_str(), outFilename.c_str()))
|
||||
throw Error("cannot rename " + tmpFilename + " to " + outFilename);
|
||||
|
||||
/* Register it with Nix. */
|
||||
registerFile(outFilename);
|
||||
|
||||
if (verbose)
|
||||
cerr << "instantiated " << (string) outHash
|
||||
<< " from " << filename << endl;
|
||||
|
||||
(*ctx.done)[filename] = outHash;
|
||||
return outHash;
|
||||
}
|
||||
|
||||
|
||||
/* Instantiate a set of Fix descriptors into Nix descriptors. */
|
||||
void instantiateDescriptors(Strings filenames)
|
||||
{
|
||||
DescriptorMap done;
|
||||
|
||||
EvalContext ctx;
|
||||
ctx.done = &done;
|
||||
|
||||
for (Strings::iterator it = filenames.begin();
|
||||
it != filenames.end(); it++)
|
||||
{
|
||||
string filename = absPath(*it);
|
||||
cout << (string) instantiateDescriptor(filename, ctx) << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Print help. */
|
||||
void printUsage()
|
||||
{
|
||||
cerr <<
|
||||
"Usage: fix ...\n\
|
||||
";
|
||||
}
|
||||
|
||||
|
||||
/* Parse the command-line arguments, call the right operation. */
|
||||
void run(Strings::iterator argCur, Strings::iterator argEnd)
|
||||
{
|
||||
umask(0022);
|
||||
|
||||
Strings extraArgs;
|
||||
enum { cmdUnknown, cmdInstantiate } command = cmdUnknown;
|
||||
|
||||
char * homeDir = getenv(nixHomeDirEnvVar.c_str());
|
||||
if (homeDir) nixHomeDir = homeDir;
|
||||
|
||||
nixDescriptorDir = nixHomeDir + "/var/nix/descriptors";
|
||||
|
||||
for ( ; argCur != argEnd; argCur++) {
|
||||
string arg(*argCur);
|
||||
if (arg == "-h" || arg == "--help") {
|
||||
printUsage();
|
||||
return;
|
||||
} else if (arg == "-v" || arg == "--verbose") {
|
||||
verbose = true;
|
||||
} else if (arg == "--instantiate" || arg == "-i") {
|
||||
command = cmdInstantiate;
|
||||
} else if (arg[0] == '-')
|
||||
throw UsageError("invalid option `" + arg + "'");
|
||||
if (ATmatch(e, "Var(<str>)", &s))
|
||||
if (x == s)
|
||||
return rep;
|
||||
else
|
||||
extraArgs.push_back(arg);
|
||||
return e;
|
||||
|
||||
if (ATmatch(e, "Lam(<str>, <term>)", &s, &e2))
|
||||
if (x == s)
|
||||
return e;
|
||||
/* !!! unfair substitutions */
|
||||
|
||||
/* Generically substitute in subterms. */
|
||||
|
||||
if (ATgetType(e) == AT_APPL) {
|
||||
AFun fun = ATgetAFun(e);
|
||||
int arity = ATgetArity(fun);
|
||||
ATermList args = ATempty;
|
||||
|
||||
for (int i = arity - 1; i >= 0; i--)
|
||||
args = ATinsert(args, substExpr(x, rep, ATgetArgument(e, i)));
|
||||
|
||||
return (ATerm) ATmakeApplList(fun, args);
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
if (ATgetType(e) == AT_LIST) {
|
||||
ATermList in = (ATermList) e;
|
||||
ATermList out = ATempty;
|
||||
|
||||
case cmdInstantiate:
|
||||
instantiateDescriptors(extraArgs);
|
||||
break;
|
||||
while (!ATisEmpty(in)) {
|
||||
out = ATinsert(out, substExpr(x, rep, ATgetFirst(in)));
|
||||
in = ATgetNext(in);
|
||||
}
|
||||
|
||||
default:
|
||||
throw UsageError("no operation specified");
|
||||
return (ATerm) ATreverse(out);
|
||||
}
|
||||
|
||||
throw badTerm("do not know how to substitute", e);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char * * argv)
|
||||
static Expr substExprMany(ATermList formals, ATermList args, Expr body)
|
||||
{
|
||||
ATerm bottomOfStack;
|
||||
ATinit(argc, argv, &bottomOfStack);
|
||||
char * s;
|
||||
Expr e;
|
||||
|
||||
/* Put the arguments in a vector. */
|
||||
Strings args;
|
||||
while (argc--) args.push_back(*argv++);
|
||||
Strings::iterator argCur = args.begin(), argEnd = args.end();
|
||||
/* !!! check args against formals */
|
||||
|
||||
argCur++;
|
||||
while (!ATisEmpty(args)) {
|
||||
ATerm tup = ATgetFirst(args);
|
||||
if (!ATmatch(tup, "(<str>, <term>)", &s, &e))
|
||||
throw badTerm("expected an argument tuple", tup);
|
||||
|
||||
body = substExpr(s, e, body);
|
||||
|
||||
args = ATgetNext(args);
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
|
||||
Hash hashPackage(EvalState & state, FState fs)
|
||||
{
|
||||
if (fs.type == FState::fsDerive) {
|
||||
for (FSIds::iterator i = fs.derive.inputs.begin();
|
||||
i != fs.derive.inputs.end(); i++)
|
||||
{
|
||||
PkgHashes::iterator j = state.pkgHashes.find(*i);
|
||||
if (j == state.pkgHashes.end())
|
||||
throw Error(format("unknown package id %1%") % (string) *i);
|
||||
*i = j->second;
|
||||
}
|
||||
}
|
||||
return hashTerm(unparseFState(fs));
|
||||
}
|
||||
|
||||
|
||||
static Expr evalExpr2(EvalState & state, Expr e)
|
||||
{
|
||||
char * s1;
|
||||
Expr e1, e2, e3, e4;
|
||||
ATermList bnds;
|
||||
|
||||
/* Normal forms. */
|
||||
if (ATmatch(e, "<str>", &s1) ||
|
||||
ATmatch(e, "Function([<list>], <term>)", &e1, &e2) ||
|
||||
ATmatch(e, "FSId(<str>)", &s1))
|
||||
return e;
|
||||
|
||||
try {
|
||||
run(argCur, argEnd);
|
||||
} catch (UsageError & e) {
|
||||
cerr << "error: " << e.what() << endl
|
||||
<< "Try `fix -h' for more information.\n";
|
||||
return 1;
|
||||
} catch (exception & e) {
|
||||
cerr << "error: " << e.what() << endl;
|
||||
return 1;
|
||||
Hash pkgHash = hashPackage(state, parseFState(e));
|
||||
FSId pkgId = writeTerm(e, "");
|
||||
state.pkgHashes[pkgId] = pkgHash;
|
||||
return ATmake("FSId(<str>)", ((string) pkgId).c_str());
|
||||
} catch (...) { /* !!! catch parse errors only */
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* Application. */
|
||||
if (ATmatch(e, "App(<term>, [<list>])", &e1, &e2)) {
|
||||
e1 = evalExpr(state, e1);
|
||||
if (!ATmatch(e1, "Function([<list>], <term>)", &e3, &e4))
|
||||
throw badTerm("expecting a function", e1);
|
||||
return evalExpr(state,
|
||||
substExprMany((ATermList) e3, (ATermList) e2, e4));
|
||||
}
|
||||
|
||||
/* Fix inclusion. */
|
||||
if (ATmatch(e, "IncludeFix(<str>)", &s1)) {
|
||||
string fileName(s1);
|
||||
return evalFile(state, s1);
|
||||
}
|
||||
|
||||
/* Relative files. */
|
||||
if (ATmatch(e, "Relative(<str>)", &s1)) {
|
||||
string srcPath = searchPath(state.searchDirs, s1);
|
||||
string dstPath;
|
||||
FSId id;
|
||||
addToStore(srcPath, dstPath, id, true);
|
||||
|
||||
SliceElem elem;
|
||||
elem.path = dstPath;
|
||||
elem.id = id;
|
||||
FState fs;
|
||||
fs.type = FState::fsSlice;
|
||||
fs.slice.roots.push_back(id);
|
||||
fs.slice.elems.push_back(elem);
|
||||
|
||||
Hash pkgHash = hashPackage(state, fs);
|
||||
FSId pkgId = writeTerm(unparseFState(fs), "");
|
||||
state.pkgHashes[pkgId] = pkgHash;
|
||||
|
||||
msg(lvlChatty, format("copied `%1%' -> %2%")
|
||||
% srcPath % (string) pkgId);
|
||||
|
||||
return ATmake("FSId(<str>)", ((string) pkgId).c_str());
|
||||
}
|
||||
|
||||
/* Packages are transformed into Derive fstate expressions. */
|
||||
if (ATmatch(e, "Package([<list>])", &bnds)) {
|
||||
|
||||
/* Evaluate the bindings and put them in a map. */
|
||||
map<string, ATerm> bndMap;
|
||||
bndMap["platform"] = ATmake("<str>", SYSTEM);
|
||||
while (!ATisEmpty(bnds)) {
|
||||
ATerm bnd = ATgetFirst(bnds);
|
||||
if (!ATmatch(bnd, "(<str>, <term>)", &s1, &e1))
|
||||
throw badTerm("binding expected", bnd);
|
||||
bndMap[s1] = evalExpr(state, e1);
|
||||
bnds = ATgetNext(bnds);
|
||||
}
|
||||
|
||||
/* Gather information for building the Derive expression. */
|
||||
FState fs;
|
||||
fs.type = FState::fsDerive;
|
||||
fs.derive.platform = SYSTEM;
|
||||
string name;
|
||||
FSId outId;
|
||||
bool outIdGiven = false;
|
||||
bnds = ATempty;
|
||||
|
||||
for (map<string, ATerm>::iterator it = bndMap.begin();
|
||||
it != bndMap.end(); it++)
|
||||
{
|
||||
string key = it->first;
|
||||
ATerm value = it->second;
|
||||
|
||||
if (ATmatch(value, "FSId(<str>)", &s1)) {
|
||||
FSId id = parseHash(s1);
|
||||
Strings paths = fstatePaths(id);
|
||||
if (paths.size() != 1) abort();
|
||||
string path = *(paths.begin());
|
||||
fs.derive.inputs.push_back(id);
|
||||
fs.derive.env.push_back(StringPair(key, path));
|
||||
if (key == "build") fs.derive.builder = path;
|
||||
}
|
||||
else if (ATmatch(value, "<str>", &s1)) {
|
||||
if (key == "name") name = s1;
|
||||
if (key == "id") {
|
||||
outId = parseHash(s1);
|
||||
outIdGiven = true;
|
||||
}
|
||||
fs.derive.env.push_back(StringPair(key, s1));
|
||||
}
|
||||
else throw badTerm("invalid package argument", value);
|
||||
|
||||
bnds = ATinsert(bnds,
|
||||
ATmake("(<str>, <term>)", key.c_str(), value));
|
||||
}
|
||||
|
||||
if (fs.derive.builder == "")
|
||||
throw badTerm("no builder specified", e);
|
||||
|
||||
if (name == "")
|
||||
throw badTerm("no package name specified", e);
|
||||
|
||||
/* Hash the fstate-expression with no outputs to produce a
|
||||
unique but deterministic path name for this package. */
|
||||
if (!outIdGiven) outId = hashPackage(state, fs);
|
||||
string outPath =
|
||||
canonPath(nixStore + "/" + ((string) outId).c_str() + "-" + name);
|
||||
fs.derive.env.push_back(StringPair("out", outPath));
|
||||
fs.derive.outputs.push_back(DeriveOutput(outPath, outId));
|
||||
|
||||
/* Write the resulting term into the Nix store directory. */
|
||||
Hash pkgHash = outIdGiven
|
||||
? hashString((string) outId + outPath)
|
||||
: hashPackage(state, fs);
|
||||
FSId pkgId = writeTerm(unparseFState(fs), "-d-" + name);
|
||||
state.pkgHashes[pkgId] = pkgHash;
|
||||
|
||||
msg(lvlChatty, format("instantiated `%1%' -> %2%")
|
||||
% name % (string) pkgId);
|
||||
|
||||
return ATmake("FSId(<str>)", ((string) pkgId).c_str());
|
||||
}
|
||||
|
||||
/* BaseName primitive function. */
|
||||
if (ATmatch(e, "BaseName(<term>)", &e1)) {
|
||||
e1 = evalExpr(state, e1);
|
||||
if (!ATmatch(e1, "<str>", &s1))
|
||||
throw badTerm("string expected", e1);
|
||||
return ATmake("<str>", baseNameOf(s1).c_str());
|
||||
}
|
||||
|
||||
/* Barf. */
|
||||
throw badTerm("invalid expression", e);
|
||||
}
|
||||
|
||||
|
||||
static Expr evalExpr(EvalState & state, Expr e)
|
||||
{
|
||||
/* Consult the memo table to quickly get the normal form of
|
||||
previously evaluated expressions. */
|
||||
NormalForms::iterator i = state.normalForms.find(e);
|
||||
if (i != state.normalForms.end()) return i->second;
|
||||
|
||||
/* Otherwise, evaluate and memoize. */
|
||||
Expr nf = evalExpr2(state, e);
|
||||
state.normalForms[e] = nf;
|
||||
return nf;
|
||||
}
|
||||
|
||||
|
||||
static Expr evalFile(EvalState & state, string relPath)
|
||||
{
|
||||
string path = searchPath(state.searchDirs, relPath);
|
||||
Nest nest(lvlTalkative, format("evaluating file `%1%'") % path);
|
||||
Expr e = ATreadFromNamedFile(path.c_str());
|
||||
if (!e)
|
||||
throw Error(format("unable to read a term from `%1%'") % path);
|
||||
return evalExpr(state, e);
|
||||
}
|
||||
|
||||
|
||||
void run(Strings args)
|
||||
{
|
||||
EvalState state;
|
||||
Strings files;
|
||||
|
||||
state.searchDirs.push_back(".");
|
||||
state.searchDirs.push_back(nixDataDir + "/fix");
|
||||
|
||||
for (Strings::iterator it = args.begin();
|
||||
it != args.end(); )
|
||||
{
|
||||
string arg = *it++;
|
||||
|
||||
if (arg == "--includedir" || arg == "-I") {
|
||||
if (it == args.end())
|
||||
throw UsageError(format("argument required in `%1%'") % arg);
|
||||
state.searchDirs.push_back(*it++);
|
||||
}
|
||||
else if (arg == "--verbose" || arg == "-v")
|
||||
verbosity = (Verbosity) ((int) verbosity + 1);
|
||||
else if (arg[0] == '-')
|
||||
throw UsageError(format("unknown flag `%1%`") % arg);
|
||||
else
|
||||
files.push_back(arg);
|
||||
}
|
||||
|
||||
if (files.empty()) throw UsageError("no files specified");
|
||||
|
||||
for (Strings::iterator it = files.begin();
|
||||
it != files.end(); it++)
|
||||
{
|
||||
Expr e = evalFile(state, *it);
|
||||
char * s;
|
||||
if (ATmatch(e, "FSId(<str>)", &s)) {
|
||||
cout << format("%1%\n") % s;
|
||||
}
|
||||
else throw badTerm("top level is not a package", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
string programId = "fix";
|
||||
|
||||
227
src/fstate.cc
Normal file
227
src/fstate.cc
Normal file
@@ -0,0 +1,227 @@
|
||||
#include "fstate.hh"
|
||||
#include "globals.hh"
|
||||
#include "store.hh"
|
||||
|
||||
|
||||
string printTerm(ATerm t)
|
||||
{
|
||||
char * s = ATwriteToString(t);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
Error badTerm(const format & f, ATerm t)
|
||||
{
|
||||
return Error(format("%1%, in `%2%'") % f.str() % printTerm(t));
|
||||
}
|
||||
|
||||
|
||||
Hash hashTerm(ATerm t)
|
||||
{
|
||||
return hashString(printTerm(t));
|
||||
}
|
||||
|
||||
|
||||
ATerm termFromId(const FSId & id)
|
||||
{
|
||||
string path = expandId(id);
|
||||
ATerm t = ATreadFromNamedFile(path.c_str());
|
||||
if (!t) throw Error(format("cannot read aterm from `%1%'") % path);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
FSId writeTerm(ATerm t, const string & suffix, FSId id)
|
||||
{
|
||||
/* By default, the id of a term is its hash. */
|
||||
if (id == FSId()) id = hashTerm(t);
|
||||
|
||||
string path = canonPath(nixStore + "/" +
|
||||
(string) id + suffix + ".nix");
|
||||
if (!ATwriteToNamedTextFile(t, path.c_str()))
|
||||
throw Error(format("cannot write aterm %1%") % path);
|
||||
|
||||
// debug(format("written term %1% = %2%") % (string) id %
|
||||
// printTerm(t));
|
||||
|
||||
registerPath(path, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
static void parseIds(ATermList ids, FSIds & out)
|
||||
{
|
||||
while (!ATisEmpty(ids)) {
|
||||
char * s;
|
||||
ATerm id = ATgetFirst(ids);
|
||||
if (!ATmatch(id, "<str>", &s))
|
||||
throw badTerm("not an id", id);
|
||||
out.push_back(parseHash(s));
|
||||
ids = ATgetNext(ids);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void checkSlice(const Slice & slice)
|
||||
{
|
||||
if (slice.elems.size() == 0)
|
||||
throw Error("empty slice");
|
||||
|
||||
FSIdSet decl;
|
||||
for (SliceElems::const_iterator i = slice.elems.begin();
|
||||
i != slice.elems.end(); i++)
|
||||
decl.insert(i->id);
|
||||
|
||||
for (FSIds::const_iterator i = slice.roots.begin();
|
||||
i != slice.roots.end(); i++)
|
||||
if (decl.find(*i) == decl.end())
|
||||
throw Error(format("undefined id: %1%") % (string) *i);
|
||||
|
||||
for (SliceElems::const_iterator i = slice.elems.begin();
|
||||
i != slice.elems.end(); i++)
|
||||
for (FSIds::const_iterator j = i->refs.begin();
|
||||
j != i->refs.end(); j++)
|
||||
if (decl.find(*j) == decl.end())
|
||||
throw Error(format("undefined id: %1%") % (string) *j);
|
||||
}
|
||||
|
||||
|
||||
/* Parse a slice. */
|
||||
static bool parseSlice(ATerm t, Slice & slice)
|
||||
{
|
||||
ATermList roots, elems;
|
||||
|
||||
if (!ATmatch(t, "Slice([<list>], [<list>])", &roots, &elems))
|
||||
return false;
|
||||
|
||||
parseIds(roots, slice.roots);
|
||||
|
||||
while (!ATisEmpty(elems)) {
|
||||
char * s1, * s2;
|
||||
ATermList refs;
|
||||
ATerm t = ATgetFirst(elems);
|
||||
if (!ATmatch(t, "(<str>, <str>, [<list>])", &s1, &s2, &refs))
|
||||
throw badTerm("not a slice element", t);
|
||||
SliceElem elem;
|
||||
elem.path = s1;
|
||||
elem.id = parseHash(s2);
|
||||
parseIds(refs, elem.refs);
|
||||
slice.elems.push_back(elem);
|
||||
elems = ATgetNext(elems);
|
||||
}
|
||||
|
||||
checkSlice(slice);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool parseDerive(ATerm t, Derive & derive)
|
||||
{
|
||||
ATermList outs, ins, bnds;
|
||||
char * builder;
|
||||
char * platform;
|
||||
|
||||
if (!ATmatch(t, "Derive([<list>], [<list>], <str>, <str>, [<list>])",
|
||||
&outs, &ins, &builder, &platform, &bnds))
|
||||
return false;
|
||||
|
||||
while (!ATisEmpty(outs)) {
|
||||
char * s1, * s2;
|
||||
ATerm t = ATgetFirst(outs);
|
||||
if (!ATmatch(t, "(<str>, <str>)", &s1, &s2))
|
||||
throw badTerm("not a derive output", t);
|
||||
derive.outputs.push_back(DeriveOutput(s1, parseHash(s2)));
|
||||
outs = ATgetNext(outs);
|
||||
}
|
||||
|
||||
parseIds(ins, derive.inputs);
|
||||
|
||||
derive.builder = builder;
|
||||
derive.platform = platform;
|
||||
|
||||
while (!ATisEmpty(bnds)) {
|
||||
char * s1, * s2;
|
||||
ATerm bnd = ATgetFirst(bnds);
|
||||
if (!ATmatch(bnd, "(<str>, <str>)", &s1, &s2))
|
||||
throw badTerm("tuple of strings expected", bnd);
|
||||
derive.env.push_back(StringPair(s1, s2));
|
||||
bnds = ATgetNext(bnds);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
FState parseFState(ATerm t)
|
||||
{
|
||||
FState fs;
|
||||
if (parseSlice(t, fs.slice))
|
||||
fs.type = FState::fsSlice;
|
||||
else if (parseDerive(t, fs.derive))
|
||||
fs.type = FState::fsDerive;
|
||||
else throw badTerm("not an fstate-expression", t);
|
||||
return fs;
|
||||
}
|
||||
|
||||
|
||||
static ATermList unparseIds(const FSIds & ids)
|
||||
{
|
||||
ATermList l = ATempty;
|
||||
for (FSIds::const_iterator i = ids.begin();
|
||||
i != ids.end(); i++)
|
||||
l = ATinsert(l,
|
||||
ATmake("<str>", ((string) *i).c_str()));
|
||||
return ATreverse(l);
|
||||
}
|
||||
|
||||
|
||||
static ATerm unparseSlice(const Slice & slice)
|
||||
{
|
||||
ATermList roots = unparseIds(slice.roots);
|
||||
|
||||
ATermList elems = ATempty;
|
||||
for (SliceElems::const_iterator i = slice.elems.begin();
|
||||
i != slice.elems.end(); i++)
|
||||
elems = ATinsert(elems,
|
||||
ATmake("(<str>, <str>, <term>)",
|
||||
i->path.c_str(),
|
||||
((string) i->id).c_str(),
|
||||
unparseIds(i->refs)));
|
||||
|
||||
return ATmake("Slice(<term>, <term>)", roots, elems);
|
||||
}
|
||||
|
||||
|
||||
static ATerm unparseDerive(const Derive & derive)
|
||||
{
|
||||
ATermList outs = ATempty;
|
||||
for (DeriveOutputs::const_iterator i = derive.outputs.begin();
|
||||
i != derive.outputs.end(); i++)
|
||||
outs = ATinsert(outs,
|
||||
ATmake("(<str>, <str>)",
|
||||
i->first.c_str(), ((string) i->second).c_str()));
|
||||
|
||||
ATermList env = ATempty;
|
||||
for (StringPairs::const_iterator i = derive.env.begin();
|
||||
i != derive.env.end(); i++)
|
||||
env = ATinsert(env,
|
||||
ATmake("(<str>, <str>)",
|
||||
i->first.c_str(), i->second.c_str()));
|
||||
|
||||
return ATmake("Derive(<term>, <term>, <str>, <str>, <term>)",
|
||||
ATreverse(outs),
|
||||
unparseIds(derive.inputs),
|
||||
derive.builder.c_str(),
|
||||
derive.platform.c_str(),
|
||||
ATreverse(env));
|
||||
}
|
||||
|
||||
|
||||
ATerm unparseFState(const FState & fs)
|
||||
{
|
||||
if (fs.type == FState::fsSlice)
|
||||
return unparseSlice(fs.slice);
|
||||
else if (fs.type == FState::fsDerive)
|
||||
return unparseDerive(fs.derive);
|
||||
else abort();
|
||||
}
|
||||
75
src/fstate.hh
Normal file
75
src/fstate.hh
Normal file
@@ -0,0 +1,75 @@
|
||||
#ifndef __FSTATE_H
|
||||
#define __FSTATE_H
|
||||
|
||||
extern "C" {
|
||||
#include <aterm2.h>
|
||||
}
|
||||
|
||||
#include "store.hh"
|
||||
|
||||
|
||||
/* Abstract syntax of fstate-expressions. */
|
||||
|
||||
typedef list<FSId> FSIds;
|
||||
|
||||
struct SliceElem
|
||||
{
|
||||
string path;
|
||||
FSId id;
|
||||
FSIds refs;
|
||||
};
|
||||
|
||||
typedef list<SliceElem> SliceElems;
|
||||
|
||||
struct Slice
|
||||
{
|
||||
FSIds roots;
|
||||
SliceElems elems;
|
||||
};
|
||||
|
||||
typedef pair<string, FSId> DeriveOutput;
|
||||
typedef pair<string, string> StringPair;
|
||||
typedef list<DeriveOutput> DeriveOutputs;
|
||||
typedef list<StringPair> StringPairs;
|
||||
|
||||
struct Derive
|
||||
{
|
||||
DeriveOutputs outputs;
|
||||
FSIds inputs;
|
||||
string builder;
|
||||
string platform;
|
||||
StringPairs env;
|
||||
};
|
||||
|
||||
struct FState
|
||||
{
|
||||
enum { fsSlice, fsDerive } type;
|
||||
Slice slice;
|
||||
Derive derive;
|
||||
};
|
||||
|
||||
|
||||
/* Return a canonical textual representation of an expression. */
|
||||
string printTerm(ATerm t);
|
||||
|
||||
/* Throw an exception with an error message containing the given
|
||||
aterm. */
|
||||
Error badTerm(const format & f, ATerm t);
|
||||
|
||||
/* Hash an aterm. */
|
||||
Hash hashTerm(ATerm t);
|
||||
|
||||
/* Read an aterm from disk, given its id. */
|
||||
ATerm termFromId(const FSId & id);
|
||||
|
||||
/* Write an aterm to the Nix store directory, and return its hash. */
|
||||
FSId writeTerm(ATerm t, const string & suffix, FSId id = FSId());
|
||||
|
||||
/* Parse an fstate-expression. */
|
||||
FState parseFState(ATerm t);
|
||||
|
||||
/* Parse an fstate-expression. */
|
||||
ATerm unparseFState(const FState & fs);
|
||||
|
||||
|
||||
#endif /* !__FSTATE_H */
|
||||
23
src/globals.cc
Normal file
23
src/globals.cc
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "globals.hh"
|
||||
#include "db.hh"
|
||||
|
||||
|
||||
string dbPath2Id = "path2id";
|
||||
string dbId2Paths = "id2paths";
|
||||
string dbSuccessors = "successors";
|
||||
string dbSubstitutes = "substitutes";
|
||||
|
||||
|
||||
string nixStore = "/UNINIT";
|
||||
string nixDataDir = "/UNINIT";
|
||||
string nixLogDir = "/UNINIT";
|
||||
string nixDB = "/UNINIT";
|
||||
|
||||
|
||||
void initDB()
|
||||
{
|
||||
createDB(nixDB, dbPath2Id);
|
||||
createDB(nixDB, dbId2Paths);
|
||||
createDB(nixDB, dbSuccessors);
|
||||
createDB(nixDB, dbSubstitutes);
|
||||
}
|
||||
72
src/globals.hh
Normal file
72
src/globals.hh
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef __GLOBALS_H
|
||||
#define __GLOBALS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
/* Database names. */
|
||||
|
||||
/* dbPath2Id :: Path -> FSId
|
||||
|
||||
Each pair (p, id) records that path $p$ contains an expansion of
|
||||
$id$. */
|
||||
extern string dbPath2Id;
|
||||
|
||||
|
||||
/* dbId2Paths :: FSId -> [Path]
|
||||
|
||||
A mapping from ids to lists of paths. */
|
||||
extern string dbId2Paths;
|
||||
|
||||
|
||||
/* dbSuccessors :: FSId -> FSId
|
||||
|
||||
Each pair $(id_1, id_2)$ in this mapping records the fact that a
|
||||
successor of an fstate expression stored in a file with identifier
|
||||
$id_1$ is stored in a file with identifier $id_2$.
|
||||
|
||||
Note that a term $y$ is successor of $x$ iff there exists a
|
||||
sequence of rewrite steps that rewrites $x$ into $y$.
|
||||
*/
|
||||
extern string dbSuccessors;
|
||||
|
||||
|
||||
/* dbSubstitutes :: FSId -> [FSId]
|
||||
|
||||
Each pair $(id, [ids])$ tells Nix that it can realise any of the
|
||||
fstate expressions referenced by the identifiers in $ids$ to
|
||||
generate a path with identifier $id$.
|
||||
|
||||
The main purpose of this is for distributed caching of derivates.
|
||||
One system can compute a derivate with hash $h$ and put it on a
|
||||
website (as a Nix archive), for instance, and then another system
|
||||
can register a substitute for that derivate. The substitute in
|
||||
this case might be an fstate expression that fetches the Nix
|
||||
archive.
|
||||
*/
|
||||
extern string dbSubstitutes;
|
||||
|
||||
|
||||
/* Path names. */
|
||||
|
||||
/* nixStore is the directory where we generally store atomic and
|
||||
derived files. */
|
||||
extern string nixStore;
|
||||
|
||||
extern string nixDataDir; /* !!! fix */
|
||||
|
||||
/* nixLogDir is the directory where we log various operations. */
|
||||
extern string nixLogDir;
|
||||
|
||||
/* nixDB is the file name of the Berkeley DB database where we
|
||||
maintain the dbXXX mappings. */
|
||||
extern string nixDB;
|
||||
|
||||
|
||||
/* Initialize the databases. */
|
||||
void initDB();
|
||||
|
||||
|
||||
#endif /* !__GLOBALS_H */
|
||||
61
src/hash.cc
61
src/hash.cc
@@ -1,20 +1,20 @@
|
||||
#include <iostream>
|
||||
|
||||
extern "C" {
|
||||
#include "md5.h"
|
||||
}
|
||||
|
||||
#include "hash.hh"
|
||||
#include <iostream>
|
||||
#include "archive.hh"
|
||||
|
||||
|
||||
/* Create a zeroed hash object. */
|
||||
Hash::Hash()
|
||||
{
|
||||
memset(hash, 0, sizeof(hash));
|
||||
}
|
||||
|
||||
|
||||
/* Check whether two hash are equal. */
|
||||
bool Hash::operator == (Hash & h2)
|
||||
bool Hash::operator == (const Hash & h2) const
|
||||
{
|
||||
for (unsigned int i = 0; i < hashSize; i++)
|
||||
if (hash[i] != h2.hash[i]) return false;
|
||||
@@ -22,14 +22,22 @@ bool Hash::operator == (Hash & h2)
|
||||
}
|
||||
|
||||
|
||||
/* Check whether two hash are not equal. */
|
||||
bool Hash::operator != (Hash & h2)
|
||||
bool Hash::operator != (const Hash & h2) const
|
||||
{
|
||||
return !(*this == h2);
|
||||
}
|
||||
|
||||
|
||||
/* Convert a hash code into a hexadecimal representation. */
|
||||
bool Hash::operator < (const Hash & h) const
|
||||
{
|
||||
for (unsigned int i = 0; i < hashSize; i++) {
|
||||
if (hash[i] < h.hash[i]) return true;
|
||||
if (hash[i] > h.hash[i]) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Hash::operator string() const
|
||||
{
|
||||
ostringstream str;
|
||||
@@ -42,10 +50,11 @@ Hash::operator string() const
|
||||
}
|
||||
|
||||
|
||||
/* Parse a hexadecimal representation of a hash code. */
|
||||
Hash parseHash(const string & s)
|
||||
{
|
||||
Hash hash;
|
||||
if (s.length() != Hash::hashSize * 2)
|
||||
throw BadRefError("invalid hash: " + s);
|
||||
for (unsigned int i = 0; i < Hash::hashSize; i++) {
|
||||
string s2(s, i * 2, 2);
|
||||
if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
|
||||
@@ -59,7 +68,6 @@ Hash parseHash(const string & s)
|
||||
}
|
||||
|
||||
|
||||
/* Verify that a reference is valid (that is, is a MD5 hash code). */
|
||||
bool isHash(const string & s)
|
||||
{
|
||||
if (s.length() != 32) return false;
|
||||
@@ -73,15 +81,44 @@ bool isHash(const string & s)
|
||||
}
|
||||
|
||||
|
||||
/* Compute the MD5 hash of a file. */
|
||||
Hash hashString(const string & s)
|
||||
{
|
||||
Hash hash;
|
||||
md5_buffer(s.c_str(), s.length(), hash.hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
Hash hashFile(const string & fileName)
|
||||
{
|
||||
Hash hash;
|
||||
FILE * file = fopen(fileName.c_str(), "rb");
|
||||
if (!file)
|
||||
throw Error("file `" + fileName + "' does not exist");
|
||||
throw SysError("file `" + fileName + "' does not exist");
|
||||
int err = md5_stream(file, hash.hash);
|
||||
fclose(file);
|
||||
if (err) throw Error("cannot hash file");
|
||||
if (err) throw SysError("cannot hash file " + fileName);
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
struct HashSink : DumpSink
|
||||
{
|
||||
struct md5_ctx ctx;
|
||||
virtual void operator ()
|
||||
(const unsigned char * data, unsigned int len)
|
||||
{
|
||||
md5_process_bytes(data, len, &ctx);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Hash hashPath(const string & path)
|
||||
{
|
||||
Hash hash;
|
||||
HashSink sink;
|
||||
md5_init_ctx(&sink.ctx);
|
||||
dumpPath(path, sink);
|
||||
md5_finish_ctx(&sink.ctx, hash.hash);
|
||||
return hash;
|
||||
}
|
||||
|
||||
28
src/hash.hh
28
src/hash.hh
@@ -13,9 +13,19 @@ struct Hash
|
||||
static const unsigned int hashSize = 16;
|
||||
unsigned char hash[hashSize];
|
||||
|
||||
/* Create a zeroed hash object. */
|
||||
Hash();
|
||||
bool operator == (Hash & h2);
|
||||
bool operator != (Hash & h2);
|
||||
|
||||
/* Check whether two hash are equal. */
|
||||
bool operator == (const Hash & h2) const;
|
||||
|
||||
/* Check whether two hash are not equal. */
|
||||
bool operator != (const Hash & h2) const;
|
||||
|
||||
/* For sorting. */
|
||||
bool operator < (const Hash & h) const;
|
||||
|
||||
/* Convert a hash code into a hexadecimal representation. */
|
||||
operator string() const;
|
||||
};
|
||||
|
||||
@@ -27,8 +37,22 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/* Parse a hexadecimal representation of a hash code. */
|
||||
Hash parseHash(const string & s);
|
||||
|
||||
/* Verify that the given string is a valid hash code. */
|
||||
bool isHash(const string & s);
|
||||
|
||||
/* Compute the hash of the given string. */
|
||||
Hash hashString(const string & s);
|
||||
|
||||
/* Compute the hash of the given file. */
|
||||
Hash hashFile(const string & fileName);
|
||||
|
||||
/* Compute the hash of the given path. The hash is defined as
|
||||
md5(dump(path)).
|
||||
*/
|
||||
Hash hashPath(const string & path);
|
||||
|
||||
|
||||
#endif /* !__HASH_H */
|
||||
|
||||
16
src/nix-hash.cc
Normal file
16
src/nix-hash.cc
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "hash.hh"
|
||||
#include "shared.hh"
|
||||
|
||||
|
||||
void run(Strings args)
|
||||
{
|
||||
for (Strings::iterator it = args.begin();
|
||||
it != args.end(); it++)
|
||||
cout << format("%1%\n") % (string) hashPath(*it);
|
||||
}
|
||||
|
||||
|
||||
string programId = "nix-hash";
|
||||
|
||||
36
src/nix-help.txt
Normal file
36
src/nix-help.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
nix [OPTIONS...] [ARGUMENTS...]
|
||||
|
||||
Operations:
|
||||
|
||||
--install / -i: realise an fstate
|
||||
--delete / -d: delete paths from the Nix store
|
||||
--add / -A: copy a path to the Nix store
|
||||
--query / -q: query information
|
||||
|
||||
--successor: register a successor expression
|
||||
--substitute: register a substitute expression
|
||||
|
||||
--dump: dump a path as a Nix archive
|
||||
--restore: restore a path from a Nix archive
|
||||
|
||||
--init: initialise the Nix database
|
||||
--verify: verify Nix structures
|
||||
|
||||
--version: output version information
|
||||
--help: display help
|
||||
|
||||
Source selection for --install, --dump:
|
||||
|
||||
--path / -p: by file name !!! -> path
|
||||
|
||||
Query flags:
|
||||
|
||||
--list / -l: query the output paths (roots) of an fstate (default)
|
||||
--requisites / -r: print all paths necessary to realise expression
|
||||
--generators / -g: find expressions producing a subset of given ids
|
||||
--expansion / -e: print a path containing id
|
||||
--graph: print a dot graph rooted at given ids
|
||||
|
||||
Options:
|
||||
|
||||
--verbose / -v: verbose operation (may be repeated)
|
||||
1088
src/nix.cc
1088
src/nix.cc
File diff suppressed because it is too large
Load Diff
329
src/normalise.cc
Normal file
329
src/normalise.cc
Normal file
@@ -0,0 +1,329 @@
|
||||
#include <map>
|
||||
|
||||
#include "normalise.hh"
|
||||
#include "references.hh"
|
||||
#include "db.hh"
|
||||
#include "exec.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
|
||||
void registerSuccessor(const FSId & id1, const FSId & id2)
|
||||
{
|
||||
setDB(nixDB, dbSuccessors, id1, id2);
|
||||
}
|
||||
|
||||
|
||||
static FSId storeSuccessor(const FSId & id1, ATerm sc)
|
||||
{
|
||||
FSId id2 = writeTerm(sc, "-s-" + (string) id1);
|
||||
registerSuccessor(id1, id2);
|
||||
return id2;
|
||||
}
|
||||
|
||||
|
||||
typedef set<FSId> FSIdSet;
|
||||
|
||||
|
||||
FSId normaliseFState(FSId id, FSIdSet pending)
|
||||
{
|
||||
Nest nest(lvlTalkative, format("normalising fstate %1%") % (string) id);
|
||||
|
||||
/* Try to substitute $id$ by any known successors in order to
|
||||
speed up the rewrite process. */
|
||||
string idSucc;
|
||||
while (queryDB(nixDB, dbSuccessors, id, idSucc)) {
|
||||
debug(format("successor %1% -> %2%") % (string) id % idSucc);
|
||||
id = parseHash(idSucc);
|
||||
}
|
||||
|
||||
/* Get the fstate expression. */
|
||||
FState fs = parseFState(termFromId(id));
|
||||
|
||||
/* It this is a normal form (i.e., a slice) we are done. */
|
||||
if (fs.type == FState::fsSlice) return id;
|
||||
|
||||
/* Otherwise, it's a derivation. */
|
||||
|
||||
/* Right platform? */
|
||||
if (fs.derive.platform != thisSystem)
|
||||
throw Error(format("a `%1%' is required, but I am a `%2%'")
|
||||
% fs.derive.platform % thisSystem);
|
||||
|
||||
/* Realise inputs (and remember all input paths). */
|
||||
typedef map<string, SliceElem> ElemMap;
|
||||
|
||||
ElemMap inMap;
|
||||
|
||||
for (FSIds::iterator i = fs.derive.inputs.begin();
|
||||
i != fs.derive.inputs.end(); i++) {
|
||||
FSId nf = normaliseFState(*i, pending);
|
||||
realiseSlice(nf, pending);
|
||||
FState fs = parseFState(termFromId(nf));
|
||||
if (fs.type != FState::fsSlice) abort();
|
||||
for (SliceElems::iterator j = fs.slice.elems.begin();
|
||||
j != fs.slice.elems.end(); j++)
|
||||
inMap[j->path] = *j;
|
||||
}
|
||||
|
||||
Strings inPaths;
|
||||
for (ElemMap::iterator i = inMap.begin(); i != inMap.end(); i++)
|
||||
inPaths.push_back(i->second.path);
|
||||
|
||||
/* Build the environment. */
|
||||
Environment env;
|
||||
for (StringPairs::iterator i = fs.derive.env.begin();
|
||||
i != fs.derive.env.end(); i++)
|
||||
env[i->first] = i->second;
|
||||
|
||||
/* Parse the outputs. */
|
||||
typedef map<string, FSId> OutPaths;
|
||||
OutPaths outPaths;
|
||||
for (DeriveOutputs::iterator i = fs.derive.outputs.begin();
|
||||
i != fs.derive.outputs.end(); i++)
|
||||
{
|
||||
debug(format("building %1% in `%2%'") % (string) i->second % i->first);
|
||||
outPaths[i->first] = i->second;
|
||||
inPaths.push_back(i->first);
|
||||
}
|
||||
|
||||
/* We can skip running the builder if we can expand all output
|
||||
paths from their ids. */
|
||||
bool fastBuild = true;
|
||||
for (OutPaths::iterator i = outPaths.begin();
|
||||
i != outPaths.end(); i++)
|
||||
{
|
||||
try {
|
||||
expandId(i->second, i->first, "/", pending);
|
||||
} catch (Error & e) {
|
||||
debug(format("fast build failed for `%1%': %2%")
|
||||
% i->first % e.what());
|
||||
fastBuild = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fastBuild) {
|
||||
|
||||
/* Check that none of the outputs exist. */
|
||||
for (OutPaths::iterator i = outPaths.begin();
|
||||
i != outPaths.end(); i++)
|
||||
if (pathExists(i->first))
|
||||
throw Error(format("path `%1%' exists") % i->first);
|
||||
|
||||
/* Run the builder. */
|
||||
msg(lvlChatty, format("building..."));
|
||||
runProgram(fs.derive.builder, env);
|
||||
msg(lvlChatty, format("build completed"));
|
||||
|
||||
} else
|
||||
msg(lvlChatty, format("fast build succesful"));
|
||||
|
||||
/* Check whether the output paths were created, and register each
|
||||
one. */
|
||||
FSIdSet used;
|
||||
for (OutPaths::iterator i = outPaths.begin();
|
||||
i != outPaths.end(); i++)
|
||||
{
|
||||
string path = i->first;
|
||||
if (!pathExists(path))
|
||||
throw Error(format("path `%1%' does not exist") % path);
|
||||
registerPath(path, i->second);
|
||||
fs.slice.roots.push_back(i->second);
|
||||
|
||||
Strings refs = filterReferences(path, inPaths);
|
||||
|
||||
SliceElem elem;
|
||||
elem.path = path;
|
||||
elem.id = i->second;
|
||||
|
||||
for (Strings::iterator j = refs.begin(); j != refs.end(); j++) {
|
||||
ElemMap::iterator k;
|
||||
OutPaths::iterator l;
|
||||
if ((k = inMap.find(*j)) != inMap.end()) {
|
||||
elem.refs.push_back(k->second.id);
|
||||
used.insert(k->second.id);
|
||||
for (FSIds::iterator m = k->second.refs.begin();
|
||||
m != k->second.refs.end(); m++)
|
||||
used.insert(*m);
|
||||
} else if ((l = outPaths.find(*j)) != outPaths.end()) {
|
||||
elem.refs.push_back(l->second);
|
||||
used.insert(l->second);
|
||||
} else
|
||||
throw Error(format("unknown referenced path `%1%'") % *j);
|
||||
}
|
||||
|
||||
fs.slice.elems.push_back(elem);
|
||||
}
|
||||
|
||||
for (ElemMap::iterator i = inMap.begin();
|
||||
i != inMap.end(); i++)
|
||||
{
|
||||
FSIdSet::iterator j = used.find(i->second.id);
|
||||
if (j == used.end())
|
||||
debug(format("NOT referenced: `%1%'") % i->second.path);
|
||||
else {
|
||||
debug(format("referenced: `%1%'") % i->second.path);
|
||||
fs.slice.elems.push_back(i->second);
|
||||
}
|
||||
}
|
||||
|
||||
fs.type = FState::fsSlice;
|
||||
ATerm nf = unparseFState(fs);
|
||||
msg(lvlVomit, format("normal form: %1%") % printTerm(nf));
|
||||
return storeSuccessor(id, nf);
|
||||
}
|
||||
|
||||
|
||||
void realiseSlice(const FSId & id, FSIdSet pending)
|
||||
{
|
||||
Nest nest(lvlDebug,
|
||||
format("realising slice %1%") % (string) id);
|
||||
|
||||
FState fs = parseFState(termFromId(id));
|
||||
if (fs.type != FState::fsSlice)
|
||||
throw Error(format("expected slice in %1%") % (string) id);
|
||||
|
||||
/* Perhaps all paths already contain the right id? */
|
||||
|
||||
bool missing = false;
|
||||
for (SliceElems::const_iterator i = fs.slice.elems.begin();
|
||||
i != fs.slice.elems.end(); i++)
|
||||
{
|
||||
SliceElem elem = *i;
|
||||
string id;
|
||||
if (!queryDB(nixDB, dbPath2Id, elem.path, id)) {
|
||||
if (pathExists(elem.path))
|
||||
throw Error(format("path `%1%' obstructed") % elem.path);
|
||||
missing = true;
|
||||
break;
|
||||
}
|
||||
if (parseHash(id) != elem.id)
|
||||
throw Error(format("path `%1%' obstructed") % elem.path);
|
||||
}
|
||||
|
||||
if (!missing) {
|
||||
debug(format("already installed"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* For each element, expand its id at its path. */
|
||||
for (SliceElems::const_iterator i = fs.slice.elems.begin();
|
||||
i != fs.slice.elems.end(); i++)
|
||||
{
|
||||
SliceElem elem = *i;
|
||||
debug(format("expanding %1% in `%2%'") % (string) elem.id % elem.path);
|
||||
expandId(elem.id, elem.path, "/", pending);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Strings fstatePaths(const FSId & id)
|
||||
{
|
||||
Strings paths;
|
||||
|
||||
FState fs = parseFState(termFromId(id));
|
||||
|
||||
if (fs.type == FState::fsSlice) {
|
||||
/* !!! fix complexity */
|
||||
for (FSIds::const_iterator i = fs.slice.roots.begin();
|
||||
i != fs.slice.roots.end(); i++)
|
||||
for (SliceElems::const_iterator j = fs.slice.elems.begin();
|
||||
j != fs.slice.elems.end(); j++)
|
||||
if (*i == j->id) paths.push_back(j->path);
|
||||
}
|
||||
|
||||
else if (fs.type == FState::fsDerive) {
|
||||
for (DeriveOutputs::iterator i = fs.derive.outputs.begin();
|
||||
i != fs.derive.outputs.end(); i++)
|
||||
paths.push_back(i->first);
|
||||
}
|
||||
|
||||
else abort();
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
static void fstateRequisitesSet(const FSId & id,
|
||||
bool includeExprs, bool includeSuccessors, StringSet & paths)
|
||||
{
|
||||
FState fs = parseFState(termFromId(id));
|
||||
|
||||
if (fs.type == FState::fsSlice) {
|
||||
for (SliceElems::iterator i = fs.slice.elems.begin();
|
||||
i != fs.slice.elems.end(); i++)
|
||||
paths.insert(i->path);
|
||||
}
|
||||
|
||||
else if (fs.type == FState::fsDerive) {
|
||||
for (FSIds::iterator i = fs.derive.inputs.begin();
|
||||
i != fs.derive.inputs.end(); i++)
|
||||
fstateRequisitesSet(*i,
|
||||
includeExprs, includeSuccessors, paths);
|
||||
}
|
||||
|
||||
else abort();
|
||||
|
||||
if (includeExprs)
|
||||
paths.insert(expandId(id));
|
||||
|
||||
string idSucc;
|
||||
if (includeSuccessors &&
|
||||
queryDB(nixDB, dbSuccessors, id, idSucc))
|
||||
fstateRequisitesSet(parseHash(idSucc),
|
||||
includeExprs, includeSuccessors, paths);
|
||||
}
|
||||
|
||||
|
||||
Strings fstateRequisites(const FSId & id,
|
||||
bool includeExprs, bool includeSuccessors)
|
||||
{
|
||||
StringSet paths;
|
||||
fstateRequisitesSet(id, includeExprs, includeSuccessors, paths);
|
||||
return Strings(paths.begin(), paths.end());
|
||||
}
|
||||
|
||||
|
||||
FSIds findGenerators(const FSIds & _ids)
|
||||
{
|
||||
FSIdSet ids(_ids.begin(), _ids.end());
|
||||
FSIds generators;
|
||||
|
||||
/* !!! hack; for performance, we just look at the rhs of successor
|
||||
mappings, since we know that those are Nix expressions. */
|
||||
|
||||
Strings sucs;
|
||||
enumDB(nixDB, dbSuccessors, sucs);
|
||||
|
||||
for (Strings::iterator i = sucs.begin();
|
||||
i != sucs.end(); i++)
|
||||
{
|
||||
string s;
|
||||
if (!queryDB(nixDB, dbSuccessors, *i, s)) continue;
|
||||
FSId id = parseHash(s);
|
||||
|
||||
FState fs;
|
||||
try {
|
||||
/* !!! should substitutes be used? */
|
||||
fs = parseFState(termFromId(id));
|
||||
} catch (...) { /* !!! only catch parse errors */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fs.type != FState::fsSlice) continue;
|
||||
|
||||
bool okay = true;
|
||||
for (SliceElems::const_iterator i = fs.slice.elems.begin();
|
||||
i != fs.slice.elems.end(); i++)
|
||||
if (ids.find(i->id) == ids.end()) {
|
||||
okay = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!okay) continue;
|
||||
|
||||
generators.push_back(id);
|
||||
}
|
||||
|
||||
return generators;
|
||||
}
|
||||
35
src/normalise.hh
Normal file
35
src/normalise.hh
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef __NORMALISE_H
|
||||
#define __NORMALISE_H
|
||||
|
||||
#include "fstate.hh"
|
||||
|
||||
|
||||
/* Normalise an fstate-expression, that is, return an equivalent
|
||||
slice. (For the meaning of `pending', see expandId()). */
|
||||
FSId normaliseFState(FSId id, FSIdSet pending = FSIdSet());
|
||||
|
||||
/* Realise a Slice in the file system. */
|
||||
void realiseSlice(const FSId & id, FSIdSet pending = FSIdSet());
|
||||
|
||||
/* Get the list of root (output) paths of the given
|
||||
fstate-expression. */
|
||||
Strings fstatePaths(const FSId & id);
|
||||
|
||||
/* Get the list of paths that are required to realise the given
|
||||
expression. For a derive expression, this is the union of
|
||||
requisites of the inputs; for a slice expression, it is the path of
|
||||
each element in the slice. If `includeExprs' is true, include the
|
||||
paths of the Nix expressions themselves. If `includeSuccessors' is
|
||||
true, include the requisites of successors. */
|
||||
Strings fstateRequisites(const FSId & id,
|
||||
bool includeExprs, bool includeSuccessors);
|
||||
|
||||
/* Return the list of the ids of all known fstate-expressions whose
|
||||
output ids are completely contained in `ids'. */
|
||||
FSIds findGenerators(const FSIds & ids);
|
||||
|
||||
/* Register a successor. */
|
||||
void registerSuccessor(const FSId & id1, const FSId & id2);
|
||||
|
||||
|
||||
#endif /* !__NORMALISE_H */
|
||||
109
src/references.cc
Normal file
109
src/references.cc
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <map>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "references.hh"
|
||||
#include "hash.hh"
|
||||
|
||||
|
||||
static void search(const string & s,
|
||||
Strings & ids, Strings & seen)
|
||||
{
|
||||
for (Strings::iterator i = ids.begin();
|
||||
i != ids.end(); )
|
||||
{
|
||||
if (s.find(*i) == string::npos)
|
||||
i++;
|
||||
else {
|
||||
debug(format("found reference to `%1%'") % *i);
|
||||
seen.push_back(*i);
|
||||
i = ids.erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void checkPath(const string & path,
|
||||
Strings & ids, Strings & seen)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
DIR * dir = opendir(path.c_str());
|
||||
|
||||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir)) {
|
||||
string name = dirent->d_name;
|
||||
if (name == "." || name == "..") continue;
|
||||
search(name, ids, seen);
|
||||
checkPath(path + "/" + name, ids, seen);
|
||||
}
|
||||
|
||||
closedir(dir); /* !!! close on exception */
|
||||
}
|
||||
|
||||
else if (S_ISREG(st.st_mode)) {
|
||||
|
||||
debug(format("checking `%1%'") % path);
|
||||
|
||||
int fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) throw SysError(format("opening file `%1%'") % path);
|
||||
|
||||
unsigned char * buf = new unsigned char[st.st_size];
|
||||
|
||||
readFull(fd, buf, st.st_size);
|
||||
|
||||
search(string((char *) buf, st.st_size), ids, seen);
|
||||
|
||||
delete buf; /* !!! autodelete */
|
||||
|
||||
close(fd); /* !!! close on exception */
|
||||
}
|
||||
|
||||
else if (S_ISLNK(st.st_mode)) {
|
||||
char buf[st.st_size];
|
||||
if (readlink(path.c_str(), buf, st.st_size) != st.st_size)
|
||||
throw SysError(format("reading symbolic link `%1%'") % path);
|
||||
search(string(buf, st.st_size), ids, seen);
|
||||
}
|
||||
|
||||
else throw Error(format("unknown file type: %1%") % path);
|
||||
}
|
||||
|
||||
|
||||
Strings filterReferences(const string & path, const Strings & paths)
|
||||
{
|
||||
map<string, string> backMap;
|
||||
Strings ids;
|
||||
Strings seen;
|
||||
|
||||
/* For efficiency (and a higher hit rate), just search for the
|
||||
hash part of the file name. (This assumes that all references
|
||||
have the form `HASH-bla'). */
|
||||
for (Strings::const_iterator i = paths.begin();
|
||||
i != paths.end(); i++)
|
||||
{
|
||||
string s = string(baseNameOf(*i), 0, 32);
|
||||
parseHash(s);
|
||||
ids.push_back(s);
|
||||
backMap[s] = *i;
|
||||
}
|
||||
|
||||
checkPath(path, ids, seen);
|
||||
|
||||
Strings found;
|
||||
for (Strings::iterator i = seen.begin(); i != seen.end(); i++)
|
||||
{
|
||||
map<string, string>::iterator j;
|
||||
if ((j = backMap.find(*i)) == backMap.end()) abort();
|
||||
found.push_back(j->second);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
10
src/references.hh
Normal file
10
src/references.hh
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __VALUES_H
|
||||
#define __VALUES_H
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
Strings filterReferences(const string & path, const Strings & refs);
|
||||
|
||||
|
||||
#endif /* !__VALUES_H */
|
||||
76
src/shared.cc
Normal file
76
src/shared.cc
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <iostream>
|
||||
#include <cctype>
|
||||
|
||||
extern "C" {
|
||||
#include <aterm2.h>
|
||||
}
|
||||
|
||||
#include "globals.hh"
|
||||
#include "shared.hh"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
||||
/* Initialize and reorder arguments, then call the actual argument
|
||||
processor. */
|
||||
static void initAndRun(int argc, char * * argv)
|
||||
{
|
||||
/* Setup Nix paths. */
|
||||
nixStore = NIX_STORE_DIR;
|
||||
nixDataDir = NIX_DATA_DIR;
|
||||
nixLogDir = NIX_LOG_DIR;
|
||||
nixDB = (string) NIX_STATE_DIR + "/nixstate.db";
|
||||
|
||||
/* Put the arguments in a vector. */
|
||||
Strings args;
|
||||
while (argc--) args.push_back(*argv++);
|
||||
args.erase(args.begin());
|
||||
|
||||
/* Expand compound dash options (i.e., `-qlf' -> `-q -l -f'). */
|
||||
for (Strings::iterator it = args.begin();
|
||||
it != args.end(); )
|
||||
{
|
||||
string arg = *it;
|
||||
if (arg.length() > 2 && arg[0] == '-' && arg[1] != '-') {
|
||||
for (unsigned int i = 1; i < arg.length(); i++)
|
||||
if (isalpha(arg[i]))
|
||||
args.insert(it, (string) "-" + arg[i]);
|
||||
else {
|
||||
args.insert(it, string(arg, i));
|
||||
break;
|
||||
}
|
||||
it = args.erase(it);
|
||||
} else it++;
|
||||
}
|
||||
|
||||
run(args);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char * * argv)
|
||||
{
|
||||
/* ATerm setup. */
|
||||
ATerm bottomOfStack;
|
||||
ATinit(argc, argv, &bottomOfStack);
|
||||
|
||||
try {
|
||||
initAndRun(argc, argv);
|
||||
} catch (UsageError & e) {
|
||||
msg(lvlError,
|
||||
format(
|
||||
"error: %1%\n"
|
||||
"Try `%2% --help' for more information.")
|
||||
% e.what() % programId);
|
||||
return 1;
|
||||
} catch (Error & e) {
|
||||
msg(lvlError, format("error: %1%") % e.msg());
|
||||
return 1;
|
||||
} catch (exception & e) {
|
||||
msg(lvlError, format("error: %1%") % e.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
15
src/shared.hh
Normal file
15
src/shared.hh
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef __SHARED_H
|
||||
#define __SHARED_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
void run(Strings args);
|
||||
|
||||
|
||||
extern string programId;
|
||||
|
||||
|
||||
#endif /* !__SHARED_H */
|
||||
382
src/store.cc
Normal file
382
src/store.cc
Normal file
@@ -0,0 +1,382 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "store.hh"
|
||||
#include "globals.hh"
|
||||
#include "db.hh"
|
||||
#include "archive.hh"
|
||||
#include "normalise.hh"
|
||||
|
||||
|
||||
struct CopySink : DumpSink
|
||||
{
|
||||
int fd;
|
||||
virtual void operator () (const unsigned char * data, unsigned int len)
|
||||
{
|
||||
writeFull(fd, data, len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct CopySource : RestoreSource
|
||||
{
|
||||
int fd;
|
||||
virtual void operator () (unsigned char * data, unsigned int len)
|
||||
{
|
||||
readFull(fd, data, len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void copyPath(string src, string dst)
|
||||
{
|
||||
/* Unfortunately C++ doesn't support coprocedures, so we have no
|
||||
nice way to chain CopySink and CopySource together. Instead we
|
||||
fork off a child to run the sink. (Fork-less platforms should
|
||||
use a thread). */
|
||||
|
||||
/* Create a pipe. */
|
||||
int fds[2];
|
||||
if (pipe(fds) == -1) throw SysError("creating pipe");
|
||||
|
||||
/* Fork. */
|
||||
pid_t pid;
|
||||
switch (pid = fork()) {
|
||||
|
||||
case -1:
|
||||
throw SysError("unable to fork");
|
||||
|
||||
case 0: /* child */
|
||||
try {
|
||||
close(fds[1]);
|
||||
CopySource source;
|
||||
source.fd = fds[0];
|
||||
restorePath(dst, source);
|
||||
_exit(0);
|
||||
} catch (exception & e) {
|
||||
cerr << "error: " << e.what() << endl;
|
||||
}
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
close(fds[0]);
|
||||
|
||||
/* Parent. */
|
||||
|
||||
CopySink sink;
|
||||
sink.fd = fds[1];
|
||||
dumpPath(src, sink);
|
||||
|
||||
/* Wait for the child to finish. */
|
||||
int status;
|
||||
if (waitpid(pid, &status, 0) != pid)
|
||||
throw SysError("waiting for child");
|
||||
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
throw Error("cannot copy file: child died");
|
||||
}
|
||||
|
||||
|
||||
void registerSubstitute(const FSId & srcId, const FSId & subId)
|
||||
{
|
||||
#if 0
|
||||
Strings subs;
|
||||
queryListDB(nixDB, dbSubstitutes, srcId, subs); /* non-existence = ok */
|
||||
|
||||
for (Strings::iterator it = subs.begin(); it != subs.end(); it++)
|
||||
if (parseHash(*it) == subId) return;
|
||||
|
||||
subs.push_back(subId);
|
||||
|
||||
setListDB(nixDB, dbSubstitutes, srcId, subs);
|
||||
#endif
|
||||
|
||||
/* For now, accept only one substitute per id. */
|
||||
Strings subs;
|
||||
subs.push_back(subId);
|
||||
setListDB(nixDB, dbSubstitutes, srcId, subs);
|
||||
}
|
||||
|
||||
|
||||
void registerPath(const string & _path, const FSId & id)
|
||||
{
|
||||
string path(canonPath(_path));
|
||||
|
||||
setDB(nixDB, dbPath2Id, path, id);
|
||||
|
||||
Strings paths;
|
||||
queryListDB(nixDB, dbId2Paths, id, paths); /* non-existence = ok */
|
||||
|
||||
for (Strings::iterator it = paths.begin();
|
||||
it != paths.end(); it++)
|
||||
if (*it == path) return;
|
||||
|
||||
paths.push_back(path);
|
||||
|
||||
setListDB(nixDB, dbId2Paths, id, paths);
|
||||
}
|
||||
|
||||
|
||||
void unregisterPath(const string & _path)
|
||||
{
|
||||
string path(canonPath(_path));
|
||||
|
||||
string _id;
|
||||
if (!queryDB(nixDB, dbPath2Id, path, _id))
|
||||
return;
|
||||
FSId id(parseHash(_id));
|
||||
|
||||
delDB(nixDB, dbPath2Id, path);
|
||||
|
||||
/* begin transaction */
|
||||
|
||||
Strings paths, paths2;
|
||||
queryListDB(nixDB, dbId2Paths, id, paths); /* non-existence = ok */
|
||||
|
||||
bool changed = false;
|
||||
for (Strings::iterator it = paths.begin();
|
||||
it != paths.end(); it++)
|
||||
if (*it != path) paths2.push_back(*it); else changed = true;
|
||||
|
||||
if (changed)
|
||||
setListDB(nixDB, dbId2Paths, id, paths2);
|
||||
|
||||
/* end transaction */
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool queryPathId(const string & path, FSId & id)
|
||||
{
|
||||
string s;
|
||||
if (!queryDB(nixDB, dbPath2Id, absPath(path), s)) return false;
|
||||
id = parseHash(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool isInPrefix(const string & path, const string & _prefix)
|
||||
{
|
||||
string prefix = canonPath(_prefix + "/");
|
||||
return string(path, 0, prefix.size()) == prefix;
|
||||
}
|
||||
|
||||
|
||||
string expandId(const FSId & id, const string & target,
|
||||
const string & prefix, FSIdSet pending)
|
||||
{
|
||||
Nest nest(lvlDebug, format("expanding %1%") % (string) id);
|
||||
|
||||
Strings paths;
|
||||
|
||||
if (!target.empty() && !isInPrefix(target, prefix))
|
||||
abort();
|
||||
|
||||
queryListDB(nixDB, dbId2Paths, id, paths);
|
||||
|
||||
/* Pick one equal to `target'. */
|
||||
if (!target.empty()) {
|
||||
|
||||
for (Strings::iterator i = paths.begin();
|
||||
i != paths.end(); i++)
|
||||
{
|
||||
string path = *i;
|
||||
if (path == target && pathExists(path))
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Arbitrarily pick the first one that exists and isn't stale. */
|
||||
for (Strings::iterator it = paths.begin();
|
||||
it != paths.end(); it++)
|
||||
{
|
||||
string path = *it;
|
||||
if (isInPrefix(path, prefix) && pathExists(path)) {
|
||||
if (target.empty())
|
||||
return path;
|
||||
else {
|
||||
copyPath(path, target);
|
||||
registerPath(target, id);
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pending.find(id) != pending.end())
|
||||
throw Error(format("id %1% already being expanded") % (string) id);
|
||||
pending.insert(id);
|
||||
|
||||
/* Try to realise the substitutes, but only if this id is not
|
||||
already being realised by a substitute. */
|
||||
Strings subs;
|
||||
queryListDB(nixDB, dbSubstitutes, id, subs); /* non-existence = ok */
|
||||
|
||||
for (Strings::iterator it = subs.begin(); it != subs.end(); it++) {
|
||||
FSId subId = parseHash(*it);
|
||||
|
||||
debug(format("trying substitute %1%") % (string) subId);
|
||||
|
||||
realiseSlice(normaliseFState(subId, pending), pending);
|
||||
|
||||
return expandId(id, target, prefix, pending);
|
||||
}
|
||||
|
||||
throw Error(format("cannot expand id `%1%'") % (string) id);
|
||||
}
|
||||
|
||||
|
||||
void addToStore(string srcPath, string & dstPath, FSId & id,
|
||||
bool deterministicName)
|
||||
{
|
||||
srcPath = absPath(srcPath);
|
||||
id = hashPath(srcPath);
|
||||
|
||||
string baseName = baseNameOf(srcPath);
|
||||
dstPath = canonPath(nixStore + "/" + (string) id + "-" + baseName);
|
||||
|
||||
try {
|
||||
/* !!! should not use the substitutes! */
|
||||
dstPath = expandId(id, deterministicName ? dstPath : "", nixStore);
|
||||
return;
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
copyPath(srcPath, dstPath);
|
||||
registerPath(dstPath, id);
|
||||
}
|
||||
|
||||
|
||||
void deleteFromStore(const string & path)
|
||||
{
|
||||
string prefix = + "/";
|
||||
if (!isInPrefix(path, nixStore))
|
||||
throw Error(format("path %1% is not in the store") % path);
|
||||
|
||||
unregisterPath(path);
|
||||
|
||||
deletePath(path);
|
||||
}
|
||||
|
||||
|
||||
void verifyStore()
|
||||
{
|
||||
Strings paths;
|
||||
enumDB(nixDB, dbPath2Id, paths);
|
||||
|
||||
for (Strings::iterator i = paths.begin();
|
||||
i != paths.end(); i++)
|
||||
{
|
||||
bool erase = true;
|
||||
string path = *i;
|
||||
|
||||
if (!pathExists(path)) {
|
||||
debug(format("path `%1%' disappeared") % path);
|
||||
}
|
||||
|
||||
else {
|
||||
string id;
|
||||
if (!queryDB(nixDB, dbPath2Id, path, id)) abort();
|
||||
|
||||
Strings idPaths;
|
||||
queryListDB(nixDB, dbId2Paths, id, idPaths);
|
||||
|
||||
bool found = false;
|
||||
for (Strings::iterator j = idPaths.begin();
|
||||
j != idPaths.end(); j++)
|
||||
if (path == *j) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
erase = false;
|
||||
else
|
||||
/* !!! perhaps we should add path to idPaths? */
|
||||
debug(format("reverse mapping for path `%1%' missing") % path);
|
||||
}
|
||||
|
||||
if (erase) delDB(nixDB, dbPath2Id, path);
|
||||
}
|
||||
|
||||
Strings ids;
|
||||
enumDB(nixDB, dbId2Paths, ids);
|
||||
|
||||
for (Strings::iterator i = ids.begin();
|
||||
i != ids.end(); i++)
|
||||
{
|
||||
FSId id = parseHash(*i);
|
||||
|
||||
Strings idPaths;
|
||||
queryListDB(nixDB, dbId2Paths, id, idPaths);
|
||||
|
||||
for (Strings::iterator j = idPaths.begin();
|
||||
j != idPaths.end(); )
|
||||
{
|
||||
string id2;
|
||||
if (!queryDB(nixDB, dbPath2Id, *j, id2) ||
|
||||
id != parseHash(id2)) {
|
||||
debug(format("erasing path `%1%' from mapping for id %2%")
|
||||
% *j % (string) id);
|
||||
j = idPaths.erase(j);
|
||||
} else j++;
|
||||
}
|
||||
|
||||
setListDB(nixDB, dbId2Paths, id, idPaths);
|
||||
}
|
||||
|
||||
|
||||
Strings subs;
|
||||
enumDB(nixDB, dbSubstitutes, subs);
|
||||
|
||||
for (Strings::iterator i = subs.begin();
|
||||
i != subs.end(); i++)
|
||||
{
|
||||
FSId srcId = parseHash(*i);
|
||||
|
||||
Strings subIds;
|
||||
queryListDB(nixDB, dbSubstitutes, srcId, subIds);
|
||||
|
||||
for (Strings::iterator j = subIds.begin();
|
||||
j != subIds.end(); )
|
||||
{
|
||||
FSId subId = parseHash(*j);
|
||||
|
||||
Strings subPaths;
|
||||
queryListDB(nixDB, dbId2Paths, subId, subPaths);
|
||||
if (subPaths.size() == 0) {
|
||||
debug(format("erasing substitute %1% for %2%")
|
||||
% (string) subId % (string) srcId);
|
||||
j = subIds.erase(j);
|
||||
} else j++;
|
||||
}
|
||||
|
||||
setListDB(nixDB, dbSubstitutes, srcId, subIds);
|
||||
}
|
||||
|
||||
Strings sucs;
|
||||
enumDB(nixDB, dbSuccessors, sucs);
|
||||
|
||||
for (Strings::iterator i = sucs.begin();
|
||||
i != sucs.end(); i++)
|
||||
{
|
||||
FSId id1 = parseHash(*i);
|
||||
|
||||
string id2;
|
||||
if (!queryDB(nixDB, dbSuccessors, id1, id2)) abort();
|
||||
|
||||
Strings id2Paths;
|
||||
queryListDB(nixDB, dbId2Paths, id2, id2Paths);
|
||||
if (id2Paths.size() == 0) {
|
||||
Strings id2Subs;
|
||||
queryListDB(nixDB, dbSubstitutes, id2, id2Subs);
|
||||
if (id2Subs.size() == 0) {
|
||||
debug(format("successor %1% for %2% missing")
|
||||
% id2 % (string) id1);
|
||||
delDB(nixDB, dbSuccessors, (string) id1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/store.hh
Normal file
51
src/store.hh
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef __STORE_H
|
||||
#define __STORE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "hash.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
typedef Hash FSId;
|
||||
|
||||
typedef set<FSId> FSIdSet;
|
||||
|
||||
|
||||
/* Copy a path recursively. */
|
||||
void copyPath(string src, string dst);
|
||||
|
||||
/* Register a substitute. */
|
||||
void registerSubstitute(const FSId & srcId, const FSId & subId);
|
||||
|
||||
/* Register a path keyed on its id. */
|
||||
void registerPath(const string & path, const FSId & id);
|
||||
|
||||
/* Query the id of a path. */
|
||||
bool queryPathId(const string & path, FSId & id);
|
||||
|
||||
/* Return a path whose contents have the given hash. If target is
|
||||
not empty, ensure that such a path is realised in target (if
|
||||
necessary by copying from another location). If prefix is not
|
||||
empty, only return a path that is an descendent of prefix.
|
||||
|
||||
The list of pending ids are those that already being expanded.
|
||||
This prevents infinite recursion for ids realised through a
|
||||
substitute (since when we build the substitute, we would first try
|
||||
to expand the id... kaboom!). */
|
||||
string expandId(const FSId & id, const string & target = "",
|
||||
const string & prefix = "/", FSIdSet pending = FSIdSet());
|
||||
|
||||
/* Copy a file to the nixStore directory and register it in dbRefs.
|
||||
Return the hash code of the value. */
|
||||
void addToStore(string srcPath, string & dstPath, FSId & id,
|
||||
bool deterministicName = false);
|
||||
|
||||
/* Delete a value from the nixStore directory. */
|
||||
void deleteFromStore(const string & path);
|
||||
|
||||
void verifyStore();
|
||||
|
||||
|
||||
#endif /* !__STORE_H */
|
||||
3
src/test-builder-1.sh
Normal file
3
src/test-builder-1.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#! /bin/sh
|
||||
|
||||
echo "Hello World" > $out
|
||||
9
src/test-builder-2.sh
Normal file
9
src/test-builder-2.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#! /bin/sh
|
||||
|
||||
echo "builder 2"
|
||||
|
||||
mkdir $out || exit 1
|
||||
cd $out || exit 1
|
||||
echo "Hallo Wereld" > bla
|
||||
echo $builder >> bla
|
||||
echo $out >> bla
|
||||
1
src/test-expr-1.nix
Normal file
1
src/test-expr-1.nix
Normal file
@@ -0,0 +1 @@
|
||||
Str("Hello World")
|
||||
192
src/test.cc
192
src/test.cc
@@ -1,16 +1,188 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "hash.hh"
|
||||
#include "archive.hh"
|
||||
#include "util.hh"
|
||||
#include "normalise.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
int main(int argc, char * * argv)
|
||||
|
||||
void realise(FSId id)
|
||||
{
|
||||
Hash h = hashFile("/etc/passwd");
|
||||
|
||||
cout << (string) h << endl;
|
||||
|
||||
h = parseHash("0b0ffd0538622bfe20b92c4aa57254d9");
|
||||
|
||||
cout << (string) h << endl;
|
||||
|
||||
return 0;
|
||||
Nest nest(lvlDebug, format("TEST: realising %1%") % (string) id);
|
||||
realiseSlice(normaliseFState(id));
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
void realiseFail(FState fs)
|
||||
{
|
||||
try {
|
||||
realiseFState(fs);
|
||||
abort();
|
||||
} catch (Error e) {
|
||||
cout << "error (expected): " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
struct MySink : DumpSink
|
||||
{
|
||||
virtual void operator () (const unsigned char * data, unsigned int len)
|
||||
{
|
||||
/* Don't use cout, it's slow as hell! */
|
||||
writeFull(STDOUT_FILENO, data, len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct MySource : RestoreSource
|
||||
{
|
||||
virtual void operator () (unsigned char * data, unsigned int len)
|
||||
{
|
||||
readFull(STDIN_FILENO, data, len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void runTests()
|
||||
{
|
||||
/* Hashing. */
|
||||
string s = "0b0ffd0538622bfe20b92c4aa57254d9";
|
||||
Hash h = parseHash(s);
|
||||
if ((string) h != s) abort();
|
||||
|
||||
try {
|
||||
h = parseHash("blah blah");
|
||||
abort();
|
||||
} catch (BadRefError err) { };
|
||||
|
||||
try {
|
||||
h = parseHash("0b0ffd0538622bfe20b92c4aa57254d99");
|
||||
abort();
|
||||
} catch (BadRefError err) { };
|
||||
|
||||
/* Path canonicalisation. */
|
||||
cout << canonPath("/./../././//") << endl;
|
||||
cout << canonPath("/foo/bar") << endl;
|
||||
cout << canonPath("///foo/////bar//") << endl;
|
||||
cout << canonPath("/././/foo/////bar//.") << endl;
|
||||
cout << canonPath("/foo////bar//..///x/") << endl;
|
||||
cout << canonPath("/foo////bar//..//..//x/y/../z/") << endl;
|
||||
cout << canonPath("/foo/bar/../../../..///") << endl;
|
||||
|
||||
/* Dumping. */
|
||||
|
||||
#if 0
|
||||
MySink sink;
|
||||
dumpPath("scratch", sink);
|
||||
cout << (string) hashPath("scratch") << endl;
|
||||
#endif
|
||||
|
||||
/* Restoring. */
|
||||
#if 0
|
||||
MySource source;
|
||||
restorePath("outdir", source);
|
||||
cout << (string) hashPath("outdir") << endl;
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* Set up the test environment. */
|
||||
|
||||
mkdir("scratch", 0777);
|
||||
|
||||
string testDir = absPath("scratch");
|
||||
cout << testDir << endl;
|
||||
|
||||
nixStore = testDir;
|
||||
nixLogDir = testDir;
|
||||
nixDB = testDir + "/db";
|
||||
|
||||
initDB();
|
||||
|
||||
/* Expression evaluation. */
|
||||
|
||||
FSId builder1id;
|
||||
string builder1fn;
|
||||
addToStore("./test-builder-1.sh", builder1fn, builder1id);
|
||||
|
||||
ATerm fs1 = ATmake(
|
||||
"Slice([<str>], [(<str>, <str>, [])])",
|
||||
((string) builder1id).c_str(),
|
||||
builder1fn.c_str(),
|
||||
((string) builder1id).c_str());
|
||||
FSId fs1id = writeTerm(fs1, "");
|
||||
|
||||
realise(fs1id);
|
||||
realise(fs1id);
|
||||
|
||||
ATerm fs2 = ATmake(
|
||||
"Slice([<str>], [(<str>, <str>, [])])",
|
||||
((string) builder1id).c_str(),
|
||||
(builder1fn + "_bla").c_str(),
|
||||
((string) builder1id).c_str());
|
||||
FSId fs2id = writeTerm(fs2, "");
|
||||
|
||||
realise(fs2id);
|
||||
realise(fs2id);
|
||||
|
||||
string out1id = hashString("foo"); /* !!! bad */
|
||||
string out1fn = nixStore + "/" + (string) out1id + "-hello.txt";
|
||||
ATerm fs3 = ATmake(
|
||||
"Derive([(<str>, <str>)], [<str>], <str>, <str>, [(\"out\", <str>)])",
|
||||
out1fn.c_str(),
|
||||
((string) out1id).c_str(),
|
||||
((string) fs1id).c_str(),
|
||||
((string) builder1fn).c_str(),
|
||||
thisSystem.c_str(),
|
||||
out1fn.c_str());
|
||||
debug(printTerm(fs3));
|
||||
FSId fs3id = writeTerm(fs3, "");
|
||||
|
||||
realise(fs3id);
|
||||
realise(fs3id);
|
||||
|
||||
|
||||
FSId builder4id;
|
||||
string builder4fn;
|
||||
addToStore("./test-builder-2.sh", builder4fn, builder4id);
|
||||
|
||||
ATerm fs4 = ATmake(
|
||||
"Slice([<str>], [(<str>, <str>, [])])",
|
||||
((string) builder4id).c_str(),
|
||||
builder4fn.c_str(),
|
||||
((string) builder4id).c_str());
|
||||
FSId fs4id = writeTerm(fs4, "");
|
||||
|
||||
realise(fs4id);
|
||||
|
||||
string out5id = hashString("bar"); /* !!! bad */
|
||||
string out5fn = nixStore + "/" + (string) out5id + "-hello2";
|
||||
ATerm fs5 = ATmake(
|
||||
"Derive([(<str>, <str>)], [<str>], <str>, <str>, [(\"out\", <str>), (\"builder\", <str>)])",
|
||||
out5fn.c_str(),
|
||||
((string) out5id).c_str(),
|
||||
((string) fs4id).c_str(),
|
||||
((string) builder4fn).c_str(),
|
||||
thisSystem.c_str(),
|
||||
out5fn.c_str(),
|
||||
((string) builder4fn).c_str());
|
||||
debug(printTerm(fs5));
|
||||
FSId fs5id = writeTerm(fs5, "");
|
||||
|
||||
realise(fs5id);
|
||||
realise(fs5id);
|
||||
}
|
||||
|
||||
|
||||
void run(Strings args)
|
||||
{
|
||||
runTests();
|
||||
}
|
||||
|
||||
|
||||
string programId = "test";
|
||||
|
||||
195
src/util.cc
195
src/util.cc
@@ -1,47 +1,192 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
|
||||
string thisSystem = SYSTEM;
|
||||
string nixHomeDir = "/nix";
|
||||
string nixHomeDirEnvVar = "NIX";
|
||||
|
||||
|
||||
|
||||
string absPath(string filename, string dir)
|
||||
Error::Error(const format & f)
|
||||
{
|
||||
if (filename[0] != '/') {
|
||||
err = f.str();
|
||||
}
|
||||
|
||||
|
||||
SysError::SysError(const format & f)
|
||||
: Error(format("%1%: %2%") % f.str() % strerror(errno))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
string absPath(string path, string dir)
|
||||
{
|
||||
if (path[0] != '/') {
|
||||
if (dir == "") {
|
||||
char buf[PATH_MAX];
|
||||
if (!getcwd(buf, sizeof(buf)))
|
||||
throw Error("cannot get cwd");
|
||||
throw SysError("cannot get cwd");
|
||||
dir = buf;
|
||||
}
|
||||
filename = dir + "/" + filename;
|
||||
/* !!! canonicalise */
|
||||
char resolved[PATH_MAX];
|
||||
if (!realpath(filename.c_str(), resolved))
|
||||
throw Error("cannot canonicalise path " + filename);
|
||||
filename = resolved;
|
||||
path = dir + "/" + path;
|
||||
}
|
||||
return filename;
|
||||
return canonPath(path);
|
||||
}
|
||||
|
||||
|
||||
/* Return the directory part of the given path, i.e., everything
|
||||
before the final `/'. */
|
||||
string dirOf(string s)
|
||||
string canonPath(const string & path)
|
||||
{
|
||||
unsigned int pos = s.rfind('/');
|
||||
if (pos == string::npos) throw Error("invalid file name");
|
||||
return string(s, 0, pos);
|
||||
string s;
|
||||
|
||||
if (path[0] != '/')
|
||||
throw Error(format("not an absolute path: `%1%'") % path);
|
||||
|
||||
string::const_iterator i = path.begin(), end = path.end();
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Skip slashes. */
|
||||
while (i != end && *i == '/') i++;
|
||||
if (i == end) break;
|
||||
|
||||
/* Ignore `.'. */
|
||||
if (*i == '.' && (i + 1 == end || i[1] == '/'))
|
||||
i++;
|
||||
|
||||
/* If `..', delete the last component. */
|
||||
else if (*i == '.' && i + 1 < end && i[1] == '.' &&
|
||||
(i + 2 == end || i[2] == '/'))
|
||||
{
|
||||
if (!s.empty()) s.erase(s.rfind('/'));
|
||||
i += 2;
|
||||
}
|
||||
|
||||
/* Normal component; copy it. */
|
||||
else {
|
||||
s += '/';
|
||||
while (i != end && *i != '/') s += *i++;
|
||||
}
|
||||
}
|
||||
|
||||
return s.empty() ? "/" : s;
|
||||
}
|
||||
|
||||
|
||||
/* Return the base name of the given path, i.e., everything following
|
||||
the final `/'. */
|
||||
string baseNameOf(string s)
|
||||
string dirOf(string path)
|
||||
{
|
||||
unsigned int pos = s.rfind('/');
|
||||
if (pos == string::npos) throw Error("invalid file name");
|
||||
return string(s, pos + 1);
|
||||
unsigned int pos = path.rfind('/');
|
||||
if (pos == string::npos)
|
||||
throw Error(format("invalid file name: %1%") % path);
|
||||
return string(path, 0, pos);
|
||||
}
|
||||
|
||||
|
||||
string baseNameOf(string path)
|
||||
{
|
||||
unsigned int pos = path.rfind('/');
|
||||
if (pos == string::npos)
|
||||
throw Error(format("invalid file name %1% ") % path);
|
||||
return string(path, pos + 1);
|
||||
}
|
||||
|
||||
|
||||
bool pathExists(const string & path)
|
||||
{
|
||||
int res;
|
||||
struct stat st;
|
||||
res = stat(path.c_str(), &st);
|
||||
if (!res) return true;
|
||||
if (errno != ENOENT)
|
||||
throw SysError(format("getting status of %1%") % path);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void deletePath(string path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path %1%") % path);
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
DIR * dir = opendir(path.c_str());
|
||||
|
||||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir)) {
|
||||
string name = dirent->d_name;
|
||||
if (name == "." || name == "..") continue;
|
||||
deletePath(path + "/" + name);
|
||||
}
|
||||
|
||||
closedir(dir); /* !!! close on exception */
|
||||
}
|
||||
|
||||
if (remove(path.c_str()) == -1)
|
||||
throw SysError(format("cannot unlink %1%") % path);
|
||||
}
|
||||
|
||||
|
||||
Verbosity verbosity = lvlError;
|
||||
|
||||
static int nestingLevel = 0;
|
||||
|
||||
|
||||
Nest::Nest(Verbosity level, const format & f)
|
||||
{
|
||||
if (level > verbosity)
|
||||
nest = false;
|
||||
else {
|
||||
msg(level, f);
|
||||
nest = true;
|
||||
nestingLevel++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Nest::~Nest()
|
||||
{
|
||||
if (nest) nestingLevel--;
|
||||
}
|
||||
|
||||
|
||||
void msg(Verbosity level, const format & f)
|
||||
{
|
||||
if (level > verbosity) return;
|
||||
string spaces;
|
||||
for (int i = 0; i < nestingLevel; i++)
|
||||
spaces += "| ";
|
||||
cerr << format("%1%%2%\n") % spaces % f.str();
|
||||
}
|
||||
|
||||
|
||||
void debug(const format & f)
|
||||
{
|
||||
msg(lvlDebug, f);
|
||||
}
|
||||
|
||||
|
||||
void readFull(int fd, unsigned char * buf, size_t count)
|
||||
{
|
||||
while (count) {
|
||||
ssize_t res = read(fd, (char *) buf, count);
|
||||
if (res == -1) throw SysError("reading from file");
|
||||
if (res == 0) throw Error("unexpected end-of-file");
|
||||
count -= res;
|
||||
buf += res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void writeFull(int fd, const unsigned char * buf, size_t count)
|
||||
{
|
||||
while (count) {
|
||||
ssize_t res = write(fd, (char *) buf, count);
|
||||
if (res == -1) throw SysError("writing to file");
|
||||
count -= res;
|
||||
buf += res;
|
||||
}
|
||||
}
|
||||
|
||||
79
src/util.hh
79
src/util.hh
@@ -2,46 +2,103 @@
|
||||
#define __UTIL_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
|
||||
|
||||
class Error : public exception
|
||||
{
|
||||
protected:
|
||||
string err;
|
||||
public:
|
||||
Error(string _err) { err = _err; }
|
||||
Error(const format & f);
|
||||
~Error() throw () { };
|
||||
const char * what() const throw () { return err.c_str(); }
|
||||
const string & msg() const throw () { return err; }
|
||||
};
|
||||
|
||||
class SysError : public Error
|
||||
{
|
||||
public:
|
||||
SysError(const format & f);
|
||||
};
|
||||
|
||||
class UsageError : public Error
|
||||
{
|
||||
public:
|
||||
UsageError(string _err) : Error(_err) { };
|
||||
UsageError(const format & f) : Error(f) { };
|
||||
};
|
||||
|
||||
|
||||
typedef vector<string> Strings;
|
||||
typedef list<string> Strings;
|
||||
typedef set<string> StringSet;
|
||||
|
||||
|
||||
/* The canonical system name, as returned by config.guess. */
|
||||
extern string thisSystem;
|
||||
|
||||
|
||||
/* The prefix of the Nix installation, and the environment variable
|
||||
that can be used to override the default. */
|
||||
extern string nixHomeDir;
|
||||
extern string nixHomeDirEnvVar;
|
||||
/* Return an absolutized path, resolving paths relative to the
|
||||
specified directory, or the current directory otherwise. The path
|
||||
is also canonicalised. */
|
||||
string absPath(string path, string dir = "");
|
||||
|
||||
/* Canonicalise a path (as in realpath(3)). */
|
||||
string canonPath(const string & path);
|
||||
|
||||
/* Return the directory part of the given path, i.e., everything
|
||||
before the final `/'. */
|
||||
string dirOf(string path);
|
||||
|
||||
/* Return the base name of the given path, i.e., everything following
|
||||
the final `/'. */
|
||||
string baseNameOf(string path);
|
||||
|
||||
/* Return true iff the given path exists. */
|
||||
bool pathExists(const string & path);
|
||||
|
||||
/* Delete a path; i.e., in the case of a directory, it is deleted
|
||||
recursively. Don't use this at home, kids. */
|
||||
void deletePath(string path);
|
||||
|
||||
|
||||
string absPath(string filename, string dir = "");
|
||||
string dirOf(string s);
|
||||
string baseNameOf(string s);
|
||||
/* Messages. */
|
||||
|
||||
typedef enum {
|
||||
lvlError,
|
||||
lvlTalkative,
|
||||
lvlChatty,
|
||||
lvlDebug,
|
||||
lvlVomit
|
||||
} Verbosity;
|
||||
|
||||
extern Verbosity verbosity; /* supress msgs > this */
|
||||
|
||||
class Nest
|
||||
{
|
||||
private:
|
||||
bool nest;
|
||||
public:
|
||||
Nest(Verbosity level, const format & f);
|
||||
~Nest();
|
||||
};
|
||||
|
||||
void msg(Verbosity level, const format & f);
|
||||
void debug(const format & f); /* short-hand for msg(lvlDebug, ...) */
|
||||
|
||||
|
||||
/* Wrappers arount read()/write() that read/write exactly the
|
||||
requested number of bytes. */
|
||||
void readFull(int fd, unsigned char * buf, size_t count);
|
||||
void writeFull(int fd, const unsigned char * buf, size_t count);
|
||||
|
||||
|
||||
#endif /* !__UTIL_H */
|
||||
|
||||
8
substitute.mk
Normal file
8
substitute.mk
Normal file
@@ -0,0 +1,8 @@
|
||||
%: %.in Makefile
|
||||
sed \
|
||||
-e s^@prefix\@^$(prefix)^g \
|
||||
-e s^@bindir\@^$(bindir)^g \
|
||||
-e s^@sysconfdir\@^$(sysconfdir)^g \
|
||||
-e s^@localstatedir\@^$(localstatedir)^g \
|
||||
< $< > $@ || rm $@
|
||||
chmod +x $@
|
||||
@@ -1,86 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
. ./settings
|
||||
|
||||
if ! ./mountloop; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Cleanup.
|
||||
rm -rf $target/dev
|
||||
rm -rf $target/proc
|
||||
|
||||
# Create the basic directory structure.
|
||||
mkdir $target
|
||||
mkdir $target/dev
|
||||
mkdir $target/proc
|
||||
mkdir $target/pkg
|
||||
mkdir $target/pkg/sys
|
||||
mkdir $target/pkg/sys/bin
|
||||
mkdir $target/pkg/sys/var
|
||||
mkdir $target/mnt
|
||||
mkdir $target/mnt/host
|
||||
mkdir -m 1777 $target/tmp
|
||||
|
||||
# Make package registrations.
|
||||
pkgdb=$target/pkg/sys/var/pkginfo
|
||||
|
||||
# Copy some programs and its libraries.
|
||||
utils="/usr/bin/vi /bin/sh /bin/mount /bin/umount /bin/ls /bin/ln /bin/cp /bin/mv /bin/rm /bin/cat /bin/df /bin/pwd /usr/bin/ld /usr/bin/as /bin/sed /bin/chmod /bin/chown /usr/bin/expr /bin/mkdir /bin/rmdir /usr/bin/sort /usr/bin/uniq /bin/uname /usr/bin/grep /bin/sleep /bin/gzip /usr/bin/make /usr/bin/cmp /bin/date /usr/bin/tr /usr/bin/ar /usr/bin/ranlib /usr/bin/basename /usr/bin/less /usr/bin/md5sum /bin/tar ../src/nix"
|
||||
bootlib=/pkg/prog-bootstrap/lib
|
||||
bootbin=/pkg/prog-bootstrap/bin
|
||||
mkdir -p $target/$bootlib
|
||||
mkdir -p $target/$bootbin
|
||||
cp -p $utils $target/$bootbin
|
||||
libs=`ldd $utils | awk '{ print $3 }' | sort | uniq`
|
||||
echo $libs
|
||||
cp -p $libs $target/$bootlib
|
||||
for i in libc.so.6 libdl.so.2 libpthread.so.0 librt.so.1 libresolv.so.2 ld-linux.so.2; do rm $target/$bootlib/$i; done
|
||||
../src/nix -d $pkgdb regpkg 5703121fe19cbeeaee7edd659cf4a25b /pkg/prog-bootstrap
|
||||
|
||||
mv $target/$bootbin/nix $target/pkg/sys/bin
|
||||
../src/nix -d $pkgdb regpkg 36bcbb801f5052739af8220c6ea51434 /pkg/sys
|
||||
|
||||
# Copy the bootstrap gcc.
|
||||
echo Copying gcc...
|
||||
rsync -a ../bootstrap/gcc/inst/pkg $target
|
||||
../src/nix -d $pkgdb regpkg 02212b3dc4e50349376975367d433929 /pkg/gcc-bootstrap
|
||||
|
||||
# Copy the bootstrap glibc.
|
||||
echo Copying glibc...
|
||||
glibcdir=/pkg/glibc-bootstrap
|
||||
rsync -a ../bootstrap/glibc/inst/pkg $target
|
||||
../src/nix -d $pkgdb regpkg c0ce03ee0bab298babbe7e3b6159d36c $glibcdir
|
||||
|
||||
# Copy the bootstrap kernel header files.
|
||||
echo Copying kernel headers...
|
||||
kerneldir=/pkg/kernel-bootstrap
|
||||
rsync -a ../bootstrap/kernel/inst/pkg $target
|
||||
../src/nix -d $pkgdb regpkg 3dc8333a2c2b4d627b892755417acf89 $kerneldir
|
||||
|
||||
# Compatibility.
|
||||
rm -rf $target/lib
|
||||
mkdir $target/lib
|
||||
ln -sf $glibcdir/lib/ld-linux.so.2 $target/lib/ld-linux.so.2
|
||||
|
||||
rm -rf $target/bin
|
||||
mkdir $target/bin
|
||||
ln -sf $bootbin/sh $target/bin/sh
|
||||
|
||||
# Build ld.so.cache.
|
||||
ldsoconf=$target/$glibcdir/etc/ld.so.conf
|
||||
echo $glibcdir/lib > $ldsoconf
|
||||
echo $bootlib >> $ldsoconf
|
||||
$target/$glibcdir/sbin/ldconfig -r $target
|
||||
|
||||
# Source repository.
|
||||
rm -f $target/src
|
||||
ln -sf /mnt/host/`pwd`/../pkg $target/src
|
||||
|
||||
# Copy boot script.
|
||||
cp -p ./start $target/pkg/sys/bin
|
||||
|
||||
# Done.
|
||||
echo Done!
|
||||
umount $target
|
||||
rmdir $target
|
||||
11
sys/makedisk
11
sys/makedisk
@@ -1,11 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
. ./settings
|
||||
|
||||
rm $image
|
||||
|
||||
dd if=/dev/zero of=$image bs=1M count=1 seek=256
|
||||
|
||||
/sbin/mke2fs -F -j $image
|
||||
/sbin/tune2fs -c 0 $image
|
||||
/sbin/tune2fs -i 0 $image
|
||||
@@ -1,8 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
. ./settings
|
||||
|
||||
mkdir $target
|
||||
if ! mount -o loop -t ext3 $image $target; then
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,5 +0,0 @@
|
||||
#! /bin/sh
|
||||
|
||||
. ./settings
|
||||
|
||||
linux ubd0=$image init=/pkg/sys/bin/start
|
||||
@@ -1,2 +0,0 @@
|
||||
image=/var/tmp/nix.img
|
||||
target=./loop
|
||||
38
sys/start
38
sys/start
@@ -1,38 +0,0 @@
|
||||
#! /pkg/prog-bootstrap/bin/sh
|
||||
|
||||
# This directory contains nix.
|
||||
export PATH=/pkg/sys/bin
|
||||
|
||||
# Add in the utilities needed for booting.
|
||||
export PATH=$PATH:`nix getpkg 5703121fe19cbeeaee7edd659cf4a25b`/bin
|
||||
|
||||
echo
|
||||
echo Starting up...
|
||||
|
||||
echo Mounting file systems...
|
||||
mount -n -o remount,rw /dev/root /
|
||||
mount -n -t proc none /proc
|
||||
mount -n -t hostfs none /mnt/host
|
||||
|
||||
echo Registering available sources...
|
||||
( if cd /src; then
|
||||
for i in *; do
|
||||
nix reg $i
|
||||
done
|
||||
fi
|
||||
)
|
||||
|
||||
export PATH=`nix getpkg coreutils-4.5.7`/bin:$PATH
|
||||
|
||||
echo
|
||||
echo "=== starting interactive shell ==="
|
||||
|
||||
sh
|
||||
|
||||
echo
|
||||
echo Shutting down...
|
||||
|
||||
umount /proc
|
||||
#sync
|
||||
mount -n -o remount,ro /dev/root /
|
||||
#sync
|
||||
Reference in New Issue
Block a user