mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-27 03:13:37 +08:00
51de26960c
Fix fork guards to work in fishd https://github.com/fish-shell/fish-shell/issues/339
987 lines
18 KiB
C++
987 lines
18 KiB
C++
/**
|
|
\file env_universal_common.c
|
|
|
|
The utility library for universal variables. Used both by the
|
|
client library and by the daemon.
|
|
|
|
*/
|
|
#include "config.h"
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <wchar.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <pwd.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <dirent.h>
|
|
#include <wctype.h>
|
|
#include <iconv.h>
|
|
|
|
#include <errno.h>
|
|
#include <locale.h>
|
|
#include <dirent.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
#include <map>
|
|
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif
|
|
|
|
#include "fallback.h"
|
|
#include "util.h"
|
|
|
|
#include "common.h"
|
|
#include "wutil.h"
|
|
#include "env_universal_common.h"
|
|
|
|
/**
|
|
Non-wide version of the set command
|
|
*/
|
|
#define SET_MBS "SET"
|
|
|
|
/**
|
|
Non-wide version of the set_export command
|
|
*/
|
|
#define SET_EXPORT_MBS "SET_EXPORT"
|
|
|
|
/**
|
|
Non-wide version of the erase command
|
|
*/
|
|
#define ERASE_MBS "ERASE"
|
|
|
|
/**
|
|
Non-wide version of the barrier command
|
|
*/
|
|
#define BARRIER_MBS "BARRIER"
|
|
|
|
/**
|
|
Non-wide version of the barrier_reply command
|
|
*/
|
|
#define BARRIER_REPLY_MBS "BARRIER_REPLY"
|
|
|
|
/**
|
|
Error message
|
|
*/
|
|
#define PARSE_ERR L"Unable to parse universal variable message: '%ls'"
|
|
|
|
/**
|
|
ERROR string for internal buffered reader
|
|
*/
|
|
#define ENV_UNIVERSAL_ERROR 0x100
|
|
|
|
/**
|
|
EAGAIN string for internal buffered reader
|
|
*/
|
|
#define ENV_UNIVERSAL_AGAIN 0x101
|
|
|
|
/**
|
|
EOF string for internal buffered reader
|
|
*/
|
|
#define ENV_UNIVERSAL_EOF 0x102
|
|
|
|
/**
|
|
A variable entry. Stores the value of a variable and whether it
|
|
should be exported. Obviously, it needs to be allocated large
|
|
enough to fit the value string.
|
|
*/
|
|
typedef struct var_uni_entry
|
|
{
|
|
int exportv; /**< Whether the variable should be exported */
|
|
wcstring val; /**< The value of the variable */
|
|
var_uni_entry():exportv(0), val() { }
|
|
}
|
|
var_uni_entry_t;
|
|
|
|
|
|
static void parse_message( wchar_t *msg,
|
|
connection_t *src );
|
|
|
|
/**
|
|
The table of all universal variables
|
|
*/
|
|
typedef std::map<wcstring, var_uni_entry_t*> env_var_table_t;
|
|
env_var_table_t env_universal_var;
|
|
|
|
/**
|
|
Callback function, should be called on all events
|
|
*/
|
|
static void (*callback)( fish_message_type_t type,
|
|
const wchar_t *key,
|
|
const wchar_t *val );
|
|
|
|
/**
|
|
List of names for the UTF-8 character set.
|
|
*/
|
|
static const char *iconv_utf8_names[]=
|
|
{
|
|
"utf-8", "UTF-8",
|
|
"utf8", "UTF8",
|
|
0
|
|
}
|
|
;
|
|
|
|
/**
|
|
List of wide character names, undefined byte length.
|
|
*/
|
|
static const char *iconv_wide_names_unknown[]=
|
|
{
|
|
"wchar_t", "WCHAR_T",
|
|
"wchar", "WCHAR",
|
|
0
|
|
}
|
|
;
|
|
|
|
/**
|
|
List of wide character names, 4 bytes long.
|
|
*/
|
|
static const char *iconv_wide_names_4[]=
|
|
{
|
|
"wchar_t", "WCHAR_T",
|
|
"wchar", "WCHAR",
|
|
"ucs-4", "UCS-4",
|
|
"ucs4", "UCS4",
|
|
"utf-32", "UTF-32",
|
|
"utf32", "UTF32",
|
|
0
|
|
}
|
|
;
|
|
|
|
/**
|
|
List of wide character names, 2 bytes long.
|
|
*/
|
|
static const char *iconv_wide_names_2[]=
|
|
{
|
|
"wchar_t", "WCHAR_T",
|
|
"wchar", "WCHAR",
|
|
"ucs-2", "UCS-2",
|
|
"ucs2", "UCS2",
|
|
"utf-16", "UTF-16",
|
|
"utf16", "UTF16",
|
|
0
|
|
}
|
|
;
|
|
|
|
template<class T>
|
|
class sloppy {};
|
|
|
|
static size_t hack_iconv(iconv_t cd, const char * const* inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
|
|
{
|
|
/* FreeBSD has this prototype: size_t iconv (iconv_t, const char **...)
|
|
OS X and Linux this one: size_t iconv (iconv_t, char **...)
|
|
AFAIK there's no single type that can be passed as both char ** and const char **.
|
|
Therefore, we let C++ figure it out, by providing a struct with an implicit conversion to both char** and const char **.
|
|
*/
|
|
struct sloppy_char
|
|
{
|
|
const char * const * t;
|
|
operator char** () const { return (char **)t; }
|
|
operator const char** () const { return (const char**)t; }
|
|
} slop_inbuf = {inbuf};
|
|
|
|
return iconv( cd, slop_inbuf, inbytesleft, outbuf, outbytesleft );
|
|
}
|
|
|
|
/**
|
|
Convert utf-8 string to wide string
|
|
*/
|
|
static wchar_t *utf2wcs( const char *in )
|
|
{
|
|
iconv_t cd=(iconv_t) -1;
|
|
int i,j;
|
|
|
|
wchar_t *out;
|
|
|
|
/*
|
|
Try to convert to wchar_t. If that is not a valid character set,
|
|
try various names for ucs-4. We can't be sure that ucs-4 is
|
|
really the character set used by wchar_t, but it is the best
|
|
assumption we can make.
|
|
*/
|
|
const char **to_name=0;
|
|
|
|
switch (sizeof (wchar_t))
|
|
{
|
|
|
|
case 2:
|
|
to_name = iconv_wide_names_2;
|
|
break;
|
|
|
|
case 4:
|
|
to_name = iconv_wide_names_4;
|
|
break;
|
|
|
|
default:
|
|
to_name = iconv_wide_names_unknown;
|
|
break;
|
|
}
|
|
|
|
|
|
/*
|
|
The line protocol fish uses is always utf-8.
|
|
*/
|
|
const char **from_name = iconv_utf8_names;
|
|
|
|
size_t in_len = strlen( in );
|
|
size_t out_len = sizeof( wchar_t )*(in_len+2);
|
|
size_t nconv;
|
|
char *nout;
|
|
|
|
out = (wchar_t *)malloc( out_len );
|
|
nout = (char *)out;
|
|
|
|
if( !out )
|
|
return 0;
|
|
|
|
for( i=0; to_name[i]; i++ )
|
|
{
|
|
for( j=0; from_name[j]; j++ )
|
|
{
|
|
cd = iconv_open ( to_name[i], from_name[j] );
|
|
|
|
if( cd != (iconv_t) -1)
|
|
{
|
|
goto start_conversion;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
start_conversion:
|
|
|
|
if (cd == (iconv_t) -1)
|
|
{
|
|
/* Something went wrong. */
|
|
debug( 0, L"Could not perform utf-8 conversion" );
|
|
if(errno != EINVAL)
|
|
wperror( L"iconv_open" );
|
|
|
|
/* Terminate the output string. */
|
|
free(out);
|
|
return 0;
|
|
}
|
|
|
|
/* FreeBSD has this prototype: size_t iconv (iconv_t, const char **...)
|
|
OS X and Linux this one: size_t iconv (iconv_t, char **...)
|
|
AFAIK there's no single type that can be passed as both char ** and const char **.
|
|
So we cast the function pointer instead (!)
|
|
*/
|
|
nconv = hack_iconv( cd, &in, &in_len, &nout, &out_len );
|
|
|
|
if (nconv == (size_t) -1)
|
|
{
|
|
debug( 0, L"Error while converting from utf string" );
|
|
return 0;
|
|
}
|
|
|
|
*((wchar_t *) nout) = L'\0';
|
|
|
|
/*
|
|
Check for silly iconv behaviour inserting an bytemark in the output
|
|
string.
|
|
*/
|
|
if (*out == L'\xfeff' || *out == L'\xffef' || *out == L'\xefbbbf')
|
|
{
|
|
wchar_t *out_old = out;
|
|
out = wcsdup(out+1);
|
|
if (! out )
|
|
{
|
|
debug(0, L"FNORD!!!!");
|
|
free( out_old );
|
|
return 0;
|
|
}
|
|
free( out_old );
|
|
}
|
|
|
|
|
|
if (iconv_close (cd) != 0)
|
|
wperror (L"iconv_close");
|
|
|
|
return out;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Convert wide string to utf-8
|
|
*/
|
|
static char *wcs2utf( const wchar_t *in )
|
|
{
|
|
iconv_t cd=(iconv_t) -1;
|
|
int i,j;
|
|
|
|
char *char_in = (char *)in;
|
|
char *out;
|
|
|
|
/*
|
|
Try to convert to wchar_t. If that is not a valid character set,
|
|
try various names for ucs-4. We can't be sure that ucs-4 is
|
|
really the character set used by wchar_t, but it is the best
|
|
assumption we can make.
|
|
*/
|
|
const char **from_name=0;
|
|
|
|
switch (sizeof (wchar_t))
|
|
{
|
|
|
|
case 2:
|
|
from_name = iconv_wide_names_2;
|
|
break;
|
|
|
|
case 4:
|
|
from_name = iconv_wide_names_4;
|
|
break;
|
|
|
|
default:
|
|
from_name = iconv_wide_names_unknown;
|
|
break;
|
|
}
|
|
|
|
const char **to_name = iconv_utf8_names;
|
|
|
|
size_t in_len = wcslen( in );
|
|
size_t out_len = sizeof( char )*( (MAX_UTF8_BYTES*in_len)+1);
|
|
size_t nconv;
|
|
char *nout;
|
|
|
|
out = (char *)malloc( out_len );
|
|
nout = (char *)out;
|
|
in_len *= sizeof( wchar_t );
|
|
|
|
if( !out )
|
|
return 0;
|
|
|
|
for( i=0; to_name[i]; i++ )
|
|
{
|
|
for( j=0; from_name[j]; j++ )
|
|
{
|
|
cd = iconv_open ( to_name[i], from_name[j] );
|
|
|
|
if( cd != (iconv_t) -1)
|
|
{
|
|
goto start_conversion;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
start_conversion:
|
|
|
|
if (cd == (iconv_t) -1)
|
|
{
|
|
/* Something went wrong. */
|
|
debug( 0, L"Could not perform utf-8 conversion" );
|
|
if(errno != EINVAL)
|
|
wperror( L"iconv_open" );
|
|
|
|
/* Terminate the output string. */
|
|
free(out);
|
|
return 0;
|
|
}
|
|
|
|
nconv = hack_iconv( cd, &char_in, &in_len, &nout, &out_len );
|
|
|
|
|
|
if (nconv == (size_t) -1)
|
|
{
|
|
debug( 0, L"%d %d", in_len, out_len );
|
|
debug( 0, L"Error while converting from to string" );
|
|
return 0;
|
|
}
|
|
|
|
*nout = '\0';
|
|
|
|
if (iconv_close (cd) != 0)
|
|
wperror (L"iconv_close");
|
|
|
|
return out;
|
|
}
|
|
|
|
|
|
|
|
void env_universal_common_init( void (*cb)(fish_message_type_t type, const wchar_t *key, const wchar_t *val ) )
|
|
{
|
|
callback = cb;
|
|
}
|
|
|
|
|
|
void env_universal_common_destroy()
|
|
{
|
|
env_var_table_t::iterator iter;
|
|
|
|
for(iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter)
|
|
{
|
|
var_uni_entry_t* value = iter->second;
|
|
delete value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Read one byte of date form the specified connection
|
|
*/
|
|
static int read_byte( connection_t *src )
|
|
{
|
|
|
|
if( src->buffer_consumed >= src->buffer_used )
|
|
{
|
|
|
|
ssize_t res = read( src->fd, src->buffer, ENV_UNIVERSAL_BUFFER_SIZE );
|
|
|
|
// debug(4, L"Read chunk '%.*s'", res, src->buffer );
|
|
|
|
if( res < 0 )
|
|
{
|
|
|
|
if( errno == EAGAIN ||
|
|
errno == EINTR )
|
|
{
|
|
return ENV_UNIVERSAL_AGAIN;
|
|
}
|
|
|
|
return ENV_UNIVERSAL_ERROR;
|
|
|
|
}
|
|
|
|
if( res == 0 )
|
|
{
|
|
return ENV_UNIVERSAL_EOF;
|
|
}
|
|
|
|
src->buffer_consumed = 0;
|
|
src->buffer_used = res;
|
|
}
|
|
|
|
return src->buffer[src->buffer_consumed++];
|
|
|
|
}
|
|
|
|
|
|
void read_message( connection_t *src )
|
|
{
|
|
while( 1 )
|
|
{
|
|
|
|
int ib = read_byte( src );
|
|
char b;
|
|
|
|
switch( ib )
|
|
{
|
|
case ENV_UNIVERSAL_AGAIN:
|
|
{
|
|
return;
|
|
}
|
|
|
|
case ENV_UNIVERSAL_ERROR:
|
|
{
|
|
debug( 2, L"Read error on fd %d, set killme flag", src->fd );
|
|
if( debug_level > 2 )
|
|
wperror( L"read" );
|
|
src->killme = 1;
|
|
return;
|
|
}
|
|
|
|
case ENV_UNIVERSAL_EOF:
|
|
{
|
|
src->killme = 1;
|
|
debug( 3, L"Fd %d has reached eof, set killme flag", src->fd );
|
|
if( src->input.size() > 0 )
|
|
{
|
|
char c = 0;
|
|
src->input.push_back(c);
|
|
debug( 1,
|
|
L"Universal variable connection closed while reading command. Partial command recieved: '%s'",
|
|
&src->input.at(0));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
b = (char)ib;
|
|
|
|
if( b == '\n' )
|
|
{
|
|
wchar_t *msg;
|
|
|
|
b = 0;
|
|
src->input.push_back(b);
|
|
|
|
msg = utf2wcs( &src->input.at(0) );
|
|
|
|
/*
|
|
Before calling parse_message, we must empty reset
|
|
everything, since the callback function could
|
|
potentially call read_message.
|
|
*/
|
|
src->input.clear();
|
|
|
|
if( msg )
|
|
{
|
|
parse_message( msg, src );
|
|
}
|
|
else
|
|
{
|
|
debug( 0, _(L"Could not convert message '%s' to wide character string"), &src->input.at(0) );
|
|
}
|
|
|
|
free( msg );
|
|
|
|
}
|
|
else
|
|
{
|
|
src->input.push_back(b);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Remove variable with specified name
|
|
*/
|
|
void env_universal_common_remove( const wcstring &name )
|
|
{
|
|
env_var_table_t::iterator result = env_universal_var.find(name);
|
|
if (result != env_universal_var.end())
|
|
{
|
|
var_uni_entry_t* v = result->second;
|
|
env_universal_var.erase(result);
|
|
delete v;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Test if the message msg contains the command cmd
|
|
*/
|
|
static int match( const wchar_t *msg, const wchar_t *cmd )
|
|
{
|
|
size_t len = wcslen( cmd );
|
|
if( wcsncasecmp( msg, cmd, len ) != 0 )
|
|
return 0;
|
|
|
|
if( msg[len] && msg[len]!= L' ' && msg[len] != L'\t' )
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void env_universal_common_set( const wchar_t *key, const wchar_t *val, int exportv )
|
|
{
|
|
var_uni_entry_t *entry;
|
|
|
|
CHECK( key, );
|
|
CHECK( val, );
|
|
|
|
entry = new var_uni_entry_t;
|
|
|
|
entry->exportv=exportv;
|
|
entry->val = val;
|
|
env_universal_common_remove( key );
|
|
|
|
env_universal_var[key] = entry;
|
|
if( callback )
|
|
{
|
|
callback( exportv?SET_EXPORT:SET, key, val );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Parse message msg
|
|
*/
|
|
static void parse_message( wchar_t *msg,
|
|
connection_t *src )
|
|
{
|
|
// debug( 3, L"parse_message( %ls );", msg );
|
|
|
|
if( msg[0] == L'#' )
|
|
return;
|
|
|
|
if( match( msg, SET_STR ) || match( msg, SET_EXPORT_STR ))
|
|
{
|
|
wchar_t *name, *tmp;
|
|
int exportv = match( msg, SET_EXPORT_STR );
|
|
|
|
name = msg+(exportv?wcslen(SET_EXPORT_STR):wcslen(SET_STR));
|
|
while( wcschr( L"\t ", *name ) )
|
|
name++;
|
|
|
|
tmp = wcschr( name, L':' );
|
|
if( tmp )
|
|
{
|
|
wchar_t *key;
|
|
wchar_t *val;
|
|
|
|
key = (wchar_t *)malloc( sizeof( wchar_t)*(tmp-name+1));
|
|
memcpy( key, name, sizeof( wchar_t)*(tmp-name));
|
|
key[tmp-name]=0;
|
|
|
|
val = tmp+1;
|
|
val = unescape( val, 0 );
|
|
|
|
if (key && val)
|
|
env_universal_common_set( key, val, exportv );
|
|
|
|
free( val );
|
|
free( key );
|
|
}
|
|
else
|
|
{
|
|
debug( 1, PARSE_ERR, msg );
|
|
}
|
|
}
|
|
else if( match( msg, ERASE_STR ) )
|
|
{
|
|
wchar_t *name, *tmp;
|
|
|
|
name = msg+wcslen(ERASE_STR);
|
|
while( wcschr( L"\t ", *name ) )
|
|
name++;
|
|
|
|
tmp = name;
|
|
while( iswalnum( *tmp ) || *tmp == L'_')
|
|
tmp++;
|
|
|
|
*tmp = 0;
|
|
|
|
if( !wcslen( name ) )
|
|
{
|
|
debug( 1, PARSE_ERR, msg );
|
|
}
|
|
|
|
env_universal_common_remove( name );
|
|
|
|
if( callback )
|
|
{
|
|
callback( ERASE, name, 0 );
|
|
}
|
|
}
|
|
else if( match( msg, BARRIER_STR) )
|
|
{
|
|
message_t *msg = create_message( BARRIER_REPLY, 0, 0 );
|
|
msg->count = 1;
|
|
src->unsent->push(msg);
|
|
try_send_all( src );
|
|
}
|
|
else if( match( msg, BARRIER_REPLY_STR ) )
|
|
{
|
|
if( callback )
|
|
{
|
|
callback( BARRIER_REPLY, 0, 0 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
debug( 1, PARSE_ERR, msg );
|
|
}
|
|
}
|
|
|
|
/**
|
|
Attempt to send the specified message to the specified file descriptor
|
|
|
|
\return 1 on sucess, 0 if the message could not be sent without blocking and -1 on error
|
|
*/
|
|
static int try_send( message_t *msg,
|
|
int fd )
|
|
{
|
|
|
|
debug( 3,
|
|
L"before write of %d chars to fd %d", msg->body.size(), fd );
|
|
|
|
ssize_t res = write( fd, msg->body.c_str(), msg->body.size() );
|
|
|
|
if( res != -1 )
|
|
{
|
|
debug( 4, L"Wrote message '%s'", msg->body.c_str() );
|
|
}
|
|
else
|
|
{
|
|
debug( 4, L"Failed to write message '%s'", msg->body.c_str() );
|
|
}
|
|
|
|
if( res == -1 )
|
|
{
|
|
switch( errno )
|
|
{
|
|
case EAGAIN:
|
|
return 0;
|
|
|
|
default:
|
|
debug( 2,
|
|
L"Error while sending universal variable message to fd %d. Closing connection",
|
|
fd );
|
|
if( debug_level > 2 )
|
|
wperror( L"write" );
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
msg->count--;
|
|
|
|
if( !msg->count )
|
|
{
|
|
delete msg;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void try_send_all( connection_t *c )
|
|
{
|
|
/* debug( 3,
|
|
L"Send all updates to connection on fd %d",
|
|
c->fd );*/
|
|
while( !c->unsent->empty() )
|
|
{
|
|
switch( try_send( c->unsent->front(), c->fd ) )
|
|
{
|
|
case 1:
|
|
c->unsent->pop();
|
|
break;
|
|
|
|
case 0:
|
|
debug( 4,
|
|
L"Socket full, send rest later" );
|
|
return;
|
|
|
|
case -1:
|
|
c->killme = 1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* The universal variable format has some funny escaping requirements; here we try to be safe */
|
|
static bool is_universal_safe_to_encode_directly(wchar_t c)
|
|
{
|
|
if (c < 32 || c > 128)
|
|
return false;
|
|
|
|
return iswalnum(c) || wcschr(L"/", c);
|
|
}
|
|
|
|
/**
|
|
Escape specified string
|
|
*/
|
|
static wcstring full_escape( const wchar_t *in )
|
|
{
|
|
wcstring out;
|
|
for( ; *in; in++ )
|
|
{
|
|
wchar_t c = *in;
|
|
if (is_universal_safe_to_encode_directly(c))
|
|
{
|
|
out.push_back(c);
|
|
}
|
|
else if (c < 256)
|
|
{
|
|
append_format(out, L"\\x%.2x", c);
|
|
}
|
|
else if (c < 65536)
|
|
{
|
|
append_format(out, L"\\u%.4x", c);
|
|
}
|
|
else
|
|
{
|
|
append_format(out, L"\\U%.8x", c);
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/* Sets the body of a message to the null-terminated list of null terminated const char *. */
|
|
void set_body(message_t *msg, ...)
|
|
{
|
|
/* Start by counting the length of all the strings */
|
|
size_t body_len = 0;
|
|
const char *arg;
|
|
va_list arg_list;
|
|
va_start(arg_list, msg);
|
|
while ((arg = va_arg(arg_list, const char *)) != NULL)
|
|
body_len += strlen(arg);
|
|
va_end(arg_list);
|
|
|
|
/* Reserve that length in the string */
|
|
msg->body.reserve(body_len + 1); //+1 for trailing NULL? Do I need that?
|
|
|
|
/* Set the string contents */
|
|
va_start(arg_list, msg);
|
|
while ((arg = va_arg(arg_list, const char *)) != NULL)
|
|
msg->body.append(arg);
|
|
va_end(arg_list);
|
|
}
|
|
|
|
/* Returns an instance of message_t allocated via new */
|
|
message_t *create_message( fish_message_type_t type,
|
|
const wchar_t *key_in,
|
|
const wchar_t *val_in )
|
|
{
|
|
message_t *msg = new message_t;
|
|
msg->count = 0;
|
|
|
|
char *key=0;
|
|
|
|
// debug( 4, L"Crete message of type %d", type );
|
|
|
|
if( key_in )
|
|
{
|
|
if( wcsvarname( key_in ) )
|
|
{
|
|
debug( 0, L"Illegal variable name: '%ls'", key_in );
|
|
return 0;
|
|
}
|
|
|
|
key = wcs2utf(key_in);
|
|
if( !key )
|
|
{
|
|
debug( 0,
|
|
L"Could not convert %ls to narrow character string",
|
|
key_in );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
switch( type )
|
|
{
|
|
case SET:
|
|
case SET_EXPORT:
|
|
{
|
|
if( !val_in )
|
|
{
|
|
val_in=L"";
|
|
}
|
|
|
|
wcstring esc = full_escape( val_in );
|
|
char *val = wcs2utf(esc.c_str());
|
|
set_body(msg, (type==SET?SET_MBS:SET_EXPORT_MBS), " ", key, ":", val, "\n", NULL);
|
|
free( val );
|
|
|
|
break;
|
|
}
|
|
|
|
case ERASE:
|
|
{
|
|
set_body(msg, ERASE_MBS, " ", key, "\n", NULL);
|
|
break;
|
|
}
|
|
|
|
case BARRIER:
|
|
{
|
|
set_body(msg, BARRIER_MBS, "\n", NULL);
|
|
break;
|
|
}
|
|
|
|
case BARRIER_REPLY:
|
|
{
|
|
set_body(msg, BARRIER_REPLY_MBS, "\n", NULL);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
debug( 0, L"create_message: Unknown message type" );
|
|
}
|
|
}
|
|
|
|
free( key );
|
|
|
|
// debug( 4, L"Message body is '%s'", msg->body );
|
|
|
|
return msg;
|
|
}
|
|
|
|
/**
|
|
Put exported or unexported variables in a string list
|
|
*/
|
|
void env_universal_common_get_names( wcstring_list_t &lst,
|
|
int show_exported,
|
|
int show_unexported )
|
|
{
|
|
env_var_table_t::const_iterator iter;
|
|
for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter)
|
|
{
|
|
const wcstring& key = iter->first;
|
|
const var_uni_entry_t *e = iter->second;
|
|
if( ( e->exportv && show_exported) ||
|
|
( !e->exportv && show_unexported) )
|
|
{
|
|
lst.push_back(key);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
wchar_t *env_universal_common_get( const wcstring &name )
|
|
{
|
|
env_var_table_t::const_iterator result = env_universal_var.find(name);
|
|
|
|
if (result != env_universal_var.end() )
|
|
{
|
|
const var_uni_entry_t *e = result->second;
|
|
if( e )
|
|
return const_cast<wchar_t*>(e->val.c_str());
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int env_universal_common_get_export( const wcstring &name )
|
|
{
|
|
env_var_table_t::const_iterator result = env_universal_var.find(name);
|
|
if (result != env_universal_var.end() )
|
|
{
|
|
const var_uni_entry_t *e = result->second;
|
|
if (e != NULL)
|
|
return e->exportv;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void enqueue_all( connection_t *c )
|
|
{
|
|
env_var_table_t::const_iterator iter;
|
|
|
|
for (iter = env_universal_var.begin(); iter != env_universal_var.end(); ++iter)
|
|
{
|
|
const wcstring &key = iter->first;
|
|
const var_uni_entry_t *val = iter->second;
|
|
|
|
message_t *msg = create_message( val->exportv?SET_EXPORT:SET, key.c_str(), val->val.c_str() );
|
|
msg->count=1;
|
|
c->unsent->push(msg);
|
|
}
|
|
|
|
try_send_all( c );
|
|
}
|
|
|
|
|
|
void connection_init( connection_t *c, int fd )
|
|
{
|
|
memset (c, 0, sizeof (connection_t));
|
|
c->fd = fd;
|
|
c->unsent = new std::queue<message_t *>;
|
|
c->buffer_consumed = c->buffer_used = 0;
|
|
}
|
|
|
|
void connection_destroy( connection_t *c)
|
|
{
|
|
if (c->unsent) delete c->unsent;
|
|
|
|
/*
|
|
A connection need not always be open - we only try to close it
|
|
if it is open.
|
|
*/
|
|
if( c->fd >= 0 )
|
|
{
|
|
if( close( c->fd ) )
|
|
{
|
|
wperror( L"close" );
|
|
}
|
|
}
|
|
}
|