2014-07-10 17:50:51 +03:00
# include "util.hh"
2023-10-25 07:43:36 +03:00
# include "fmt.hh"
2014-07-10 17:50:51 +03:00
2021-09-08 13:20:08 +03:00
# include <array>
2017-01-17 19:21:02 +02:00
# include <cctype>
# include <iostream>
2023-10-25 07:43:36 +03:00
# include <grp.h>
2021-08-28 23:26:53 +03:00
# include <regex>
2023-02-10 15:38:14 +02:00
2023-10-25 07:43:36 +03:00
namespace nix {
2014-07-10 17:50:51 +03:00
2023-10-25 07:43:36 +03:00
void initLibUtil ( ) {
// Check that exception handling works. Exception handling has been observed
// not to work on darwin when the linker flags aren't quite right.
// In this case we don't want to expose the user to some unrelated uncaught
// exception, but rather tell them exactly that exception handling is
// broken.
// When exception handling fails, the message tends to be printed by the
// C++ runtime, followed by an abort.
// For example on macOS we might see an error such as
// libc++abi: terminating with uncaught exception of type nix::SysError: error: C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded.
bool caught = false ;
try {
throwExceptionSelfCheck ( ) ;
} catch ( const nix : : Error & _e ) {
caught = true ;
}
// This is not actually the main point of this check, but let's make sure anyway:
assert ( caught ) ;
2014-07-10 17:50:51 +03:00
}
2023-10-25 07:43:36 +03:00
//////////////////////////////////////////////////////////////////////
2014-07-10 17:50:51 +03:00
2015-06-09 11:50:55 +03:00
std : : vector < char * > stringsToCharPtrs ( const Strings & ss )
2014-12-12 16:01:16 +02:00
{
2015-06-09 11:50:55 +03:00
std : : vector < char * > res ;
for ( auto & s : ss ) res . push_back ( ( char * ) s . c_str ( ) ) ;
2014-12-12 16:01:16 +02:00
res . push_back ( 0 ) ;
return res ;
}
2004-06-20 16:37:51 +03:00
2004-06-22 12:51:44 +03:00
//////////////////////////////////////////////////////////////////////
2022-01-12 17:02:29 +02:00
template < class C > C tokenizeString ( std : : string_view s , std : : string_view separators )
2005-09-22 18:43:22 +03:00
{
2012-09-19 22:43:23 +03:00
C result ;
2022-02-25 17:00:00 +02:00
auto pos = s . find_first_not_of ( separators , 0 ) ;
2022-04-05 23:13:45 +03:00
while ( pos ! = std : : string_view : : npos ) {
2022-02-25 17:00:00 +02:00
auto end = s . find_first_of ( separators , pos + 1 ) ;
2022-04-05 23:13:45 +03:00
if ( end = = std : : string_view : : npos ) end = s . size ( ) ;
2022-02-25 17:00:00 +02:00
result . insert ( result . end ( ) , std : : string ( s , pos , end - pos ) ) ;
2005-09-22 18:43:22 +03:00
pos = s . find_first_not_of ( separators , end ) ;
}
return result ;
}
2022-01-12 17:02:29 +02:00
template Strings tokenizeString ( std : : string_view s , std : : string_view separators ) ;
template StringSet tokenizeString ( std : : string_view s , std : : string_view separators ) ;
2022-02-25 17:00:00 +02:00
template std : : vector < std : : string > tokenizeString ( std : : string_view s , std : : string_view separators ) ;
2012-09-19 22:43:23 +03:00
2005-09-22 18:43:22 +03:00
2022-02-25 17:00:00 +02:00
std : : string chomp ( std : : string_view s )
2012-08-01 18:19:24 +03:00
{
size_t i = s . find_last_not_of ( " \n \r \t " ) ;
2022-02-25 17:00:00 +02:00
return i = = std : : string_view : : npos ? " " : std : : string ( s , 0 , i + 1 ) ;
2012-08-01 18:19:24 +03:00
}
2022-02-25 17:00:00 +02:00
std : : string trim ( std : : string_view s , std : : string_view whitespace )
2015-04-09 12:42:04 +03:00
{
auto i = s . find_first_not_of ( whitespace ) ;
2022-03-03 12:11:16 +02:00
if ( i = = s . npos ) return " " ;
2015-04-09 12:42:04 +03:00
auto j = s . find_last_not_of ( whitespace ) ;
2022-03-03 12:11:16 +02:00
return std : : string ( s , i , j = = s . npos ? j : j - i + 1 ) ;
2015-04-09 12:42:04 +03:00
}
2022-02-25 17:00:00 +02:00
std : : string replaceStrings (
std : : string res ,
std : : string_view from ,
std : : string_view to )
2015-06-17 17:20:11 +03:00
{
2020-11-10 15:59:03 +02:00
if ( from . empty ( ) ) return res ;
2015-06-17 17:20:11 +03:00
size_t pos = 0 ;
while ( ( pos = res . find ( from , pos ) ) ! = std : : string : : npos ) {
res . replace ( pos , from . size ( ) , to ) ;
pos + = to . size ( ) ;
}
return res ;
}
2022-02-25 17:00:00 +02:00
std : : string rewriteStrings ( std : : string s , const StringMap & rewrites )
2018-03-30 01:56:13 +03:00
{
for ( auto & i : rewrites ) {
if ( i . first = = i . second ) continue ;
size_t j = 0 ;
2022-02-25 17:00:00 +02:00
while ( ( j = s . find ( i . first , j ) ) ! = std : : string : : npos )
2018-03-30 01:56:13 +03:00
s . replace ( j , i . first . size ( ) , i . second ) ;
}
return s ;
}
2020-06-13 00:12:36 +03:00
bool hasPrefix ( std : : string_view s , std : : string_view prefix )
2016-04-29 22:04:40 +03:00
{
2017-05-01 18:28:19 +03:00
return s . compare ( 0 , prefix . size ( ) , prefix ) = = 0 ;
2016-04-29 22:04:40 +03:00
}
2019-12-05 20:11:09 +02:00
bool hasSuffix ( std : : string_view s , std : : string_view suffix )
2008-08-25 16:31:57 +03:00
{
2019-12-05 20:11:09 +02:00
return s . size ( ) > = suffix . size ( )
& & s . substr ( s . size ( ) - suffix . size ( ) ) = = suffix ;
2008-08-25 16:31:57 +03:00
}
2016-09-14 15:42:15 +03:00
std : : string toLower ( const std : : string & s )
{
std : : string r ( s ) ;
for ( auto & c : r )
c = std : : tolower ( c ) ;
return r ;
}
2022-01-21 18:55:51 +02:00
std : : string shellEscape ( const std : : string_view s )
2017-10-25 14:01:50 +03:00
{
2022-01-21 18:55:51 +02:00
std : : string r ;
r . reserve ( s . size ( ) + 2 ) ;
r + = " ' " ;
2017-10-25 14:01:50 +03:00
for ( auto & i : s )
if ( i = = ' \' ' ) r + = " ' \\ '' " ; else r + = i ;
r + = ' \' ' ;
return r ;
}
2021-08-28 23:26:53 +03:00
/* Recreate the effect of the perl shellwords function, breaking up a
* string into arguments like a shell word , including escapes
*/
std : : vector < std : : string > shellwords2 ( const std : : string & s )
{
std : : regex whitespace ( " ^( \\ s+).* " ) ;
auto begin = s . cbegin ( ) ;
std : : vector < std : : string > res ;
std : : string cur ;
enum state {
sBegin ,
sQuote
} ;
state st = sBegin ;
auto it = begin ;
for ( ; it ! = s . cend ( ) ; + + it ) {
if ( st = = sBegin ) {
std : : smatch match ;
if ( regex_search ( it , s . cend ( ) , match , whitespace ) ) {
cur . append ( begin , it ) ;
res . push_back ( cur ) ;
cur . clear ( ) ;
it = match [ 1 ] . second ;
begin = it ;
}
}
switch ( * it ) {
case ' " ' :
cur . append ( begin , it ) ;
begin = it + 1 ;
st = st = = sBegin ? sQuote : sBegin ;
break ;
case ' \\ ' :
/* perl shellwords mostly just treats the next char as part of the string with no special processing */
cur . append ( begin , it ) ;
begin = + + it ;
break ;
}
}
cur . append ( begin , it ) ;
if ( ! cur . empty ( ) ) res . push_back ( cur ) ;
return res ;
}
2017-10-25 14:01:50 +03:00
2022-12-02 16:03:40 +02:00
void ignoreException ( Verbosity lvl )
2007-05-01 18:16:17 +03:00
{
2022-02-07 18:14:57 +02:00
/* Make sure no exceptions leave this function.
printError ( ) also throws when remote is closed . */
2007-05-01 18:16:17 +03:00
try {
2022-02-07 18:14:57 +02:00
try {
throw ;
} catch ( std : : exception & e ) {
2022-12-02 16:03:40 +02:00
printMsg ( lvl , " error (ignored): %1% " , e . what ( ) ) ;
2022-02-07 18:14:57 +02:00
}
} catch ( . . . ) { }
2007-05-01 18:16:17 +03:00
}
2014-08-20 17:01:16 +03:00
2021-10-17 10:51:33 +03:00
constexpr char base64Chars [ ] = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ " ;
2015-02-09 16:09:39 +02:00
2022-02-25 17:00:00 +02:00
std : : string base64Encode ( std : : string_view s )
2015-02-09 16:09:39 +02:00
{
2022-02-25 17:00:00 +02:00
std : : string res ;
2022-04-05 22:16:11 +03:00
res . reserve ( ( s . size ( ) + 2 ) / 3 * 4 ) ;
2015-02-09 16:09:39 +02:00
int data = 0 , nbits = 0 ;
for ( char c : s ) {
data = data < < 8 | ( unsigned char ) c ;
nbits + = 8 ;
while ( nbits > = 6 ) {
nbits - = 6 ;
res . push_back ( base64Chars [ data > > nbits & 0x3f ] ) ;
}
}
if ( nbits ) res . push_back ( base64Chars [ data < < ( 6 - nbits ) & 0x3f ] ) ;
while ( res . size ( ) % 4 ) res . push_back ( ' = ' ) ;
return res ;
}
2022-02-25 17:00:00 +02:00
std : : string base64Decode ( std : : string_view s )
2015-02-09 16:09:39 +02:00
{
2021-10-17 10:51:33 +03:00
constexpr char npos = - 1 ;
constexpr std : : array < char , 256 > base64DecodeChars = [ & ] ( ) {
std : : array < char , 256 > result { } ;
for ( auto & c : result )
c = npos ;
2015-02-09 16:09:39 +02:00
for ( int i = 0 ; i < 64 ; i + + )
2021-10-17 10:51:33 +03:00
result [ base64Chars [ i ] ] = i ;
return result ;
} ( ) ;
2015-02-09 16:09:39 +02:00
2022-02-25 17:00:00 +02:00
std : : string res ;
2022-04-05 22:16:11 +03:00
// Some sequences are missing the padding consisting of up to two '='.
// vvv
res . reserve ( ( s . size ( ) + 2 ) / 4 * 3 ) ;
2015-02-09 16:09:39 +02:00
unsigned int d = 0 , bits = 0 ;
for ( char c : s ) {
if ( c = = ' = ' ) break ;
if ( c = = ' \n ' ) continue ;
2021-07-30 21:08:54 +03:00
char digit = base64DecodeChars [ ( unsigned char ) c ] ;
2021-10-17 10:51:33 +03:00
if ( digit = = npos )
2020-07-02 00:32:06 +03:00
throw Error ( " invalid character in Base64 string: '%c' " , c ) ;
2015-02-09 16:09:39 +02:00
bits + = 6 ;
d = d < < 6 | digit ;
if ( bits > = 8 ) {
res . push_back ( d > > ( bits - 8 ) & 0xff ) ;
bits - = 8 ;
}
}
return res ;
}
2020-08-20 13:21:46 +03:00
std : : string stripIndentation ( std : : string_view s )
{
size_t minIndent = 10000 ;
size_t curIndent = 0 ;
bool atStartOfLine = true ;
for ( auto & c : s ) {
if ( atStartOfLine & & c = = ' ' )
curIndent + + ;
else if ( c = = ' \n ' ) {
if ( atStartOfLine )
minIndent = std : : max ( minIndent , curIndent ) ;
curIndent = 0 ;
atStartOfLine = true ;
} else {
if ( atStartOfLine ) {
minIndent = std : : min ( minIndent , curIndent ) ;
atStartOfLine = false ;
}
}
}
std : : string res ;
size_t pos = 0 ;
while ( pos < s . size ( ) ) {
auto eol = s . find ( ' \n ' , pos ) ;
if ( eol = = s . npos ) eol = s . size ( ) ;
if ( eol - pos > minIndent )
res . append ( s . substr ( pos + minIndent , eol - pos - minIndent ) ) ;
res . push_back ( ' \n ' ) ;
pos = eol + 1 ;
}
return res ;
}
2022-12-07 13:58:58 +02:00
std : : pair < std : : string_view , std : : string_view > getLine ( std : : string_view s )
{
auto newline = s . find ( ' \n ' ) ;
if ( newline = = s . npos ) {
return { s , " " } ;
} else {
auto line = s . substr ( 0 , newline ) ;
if ( ! line . empty ( ) & & line [ line . size ( ) - 1 ] = = ' \r ' )
line = line . substr ( 0 , line . size ( ) - 1 ) ;
return { line , s . substr ( newline + 1 ) } ;
}
}
2022-02-25 17:00:00 +02:00
std : : string showBytes ( uint64_t bytes )
2020-10-06 11:40:49 +03:00
{
return fmt ( " %.2f MiB " , bytes / ( 1024.0 * 1024.0 ) ) ;
}
2006-09-05 00:06:23 +03:00
}