aboutsummaryrefslogtreecommitdiffhomepage
path: root/builtin_set.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2011-12-26 19:11:54 -0800
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2011-12-26 19:11:54 -0800
commit3f16ace6784caab54fb054836ee93902e9701913 (patch)
tree6ae6170f86bd45ce7fd0dae4a4242bb8dc67c505 /builtin_set.cpp
parent834ea94eb97d37c65fcbf2fcc3b69303f6fb7e24 (diff)
Initial C++ conversion
Diffstat (limited to 'builtin_set.cpp')
-rw-r--r--builtin_set.cpp879
1 files changed, 879 insertions, 0 deletions
diff --git a/builtin_set.cpp b/builtin_set.cpp
new file mode 100644
index 00000000..7fa41fa9
--- /dev/null
+++ b/builtin_set.cpp
@@ -0,0 +1,879 @@
+/** \file builtin_set.c Functions defining the set builtin
+
+Functions used for implementing the set builtin.
+
+*/
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <signal.h>
+
+#include "fallback.h"
+#include "util.h"
+
+#include "wutil.h"
+#include "builtin.h"
+#include "env.h"
+#include "expand.h"
+#include "common.h"
+#include "wgetopt.h"
+#include "proc.h"
+#include "parser.h"
+
+
+/**
+ Error message for invalid path operations
+*/
+#define BUILTIN_SET_PATH_ERROR L"%ls: Could not add component %ls to %ls.\n"
+
+/**
+ Hint for invalid path operation with a colon
+*/
+#define BUILTIN_SET_PATH_HINT L"%ls: Did you mean 'set %ls $%ls %ls'?\n"
+
+/**
+ Error for mismatch between index count and elements
+*/
+#define BUILTIN_SET_ARG_COUNT L"%ls: The number of variable indexes does not match the number of values\n"
+
+/**
+ Test if the specified variable should be subject to path validation
+*/
+static int is_path_variable( const wchar_t *env )
+{
+ return contains( env,
+ L"PATH",
+ L"CDPATH" );
+}
+
+/**
+ Call env_set. If this is a path variable, e.g. PATH, validate the
+ elements. On error, print a description of the problem to stderr.
+*/
+static int my_env_set( const wchar_t *key, array_list_t *val, int scope )
+{
+ string_buffer_t sb;
+ int i;
+ int retcode = 0;
+ wchar_t *val_str=0;
+
+ if( is_path_variable( key ) )
+ {
+ int error = 0;
+
+ for( i=0; i<al_get_count( val ); i++ )
+ {
+ int show_perror = 0;
+ int show_hint = 0;
+
+ struct stat buff;
+ wchar_t *dir = (wchar_t *)al_get( val, i );
+
+ if( wstat( dir, &buff ) )
+ {
+ error = 1;
+ show_perror = 1;
+ }
+
+ if( !( S_ISDIR(buff.st_mode) ) )
+ {
+ error = 1;
+
+ }
+
+ if( error )
+ {
+ wchar_t *colon;
+
+ sb_printf( sb_err,
+ _(BUILTIN_SET_PATH_ERROR),
+ L"set",
+ dir,
+ key );
+
+ colon = wcschr( dir, L':' );
+
+ if( colon && *(colon+1) )
+ {
+ show_hint = 1;
+ }
+
+ }
+
+ if( show_perror )
+ {
+ builtin_wperror( L"set" );
+ }
+
+ if( show_hint )
+ {
+ sb_printf( sb_err,
+ _(BUILTIN_SET_PATH_HINT),
+ L"set",
+ key,
+ key,
+ wcschr( dir, L':' )+1);
+ }
+
+ if( error )
+ {
+ break;
+ }
+
+ }
+
+ if( error )
+ {
+ return 1;
+ }
+
+ }
+
+ sb_init( &sb );
+
+ if( al_get_count( val ) )
+ {
+ for( i=0; i<al_get_count( val ); i++ )
+ {
+ wchar_t *next =(wchar_t *)al_get( val, i );
+ sb_append( &sb, next?next:L"" );
+ if( i<al_get_count( val )-1 )
+ {
+ sb_append( &sb, ARRAY_SEP_STR );
+ }
+ }
+ val_str = (wchar_t *)sb.buff;
+
+ }
+
+ switch( env_set( key, val_str, scope | ENV_USER ) )
+ {
+ case ENV_PERM:
+ {
+ sb_printf( sb_err, _(L"%ls: Tried to change the read-only variable '%ls'\n"), L"set", key );
+ retcode=1;
+ break;
+ }
+
+ case ENV_INVALID:
+ {
+ sb_printf( sb_err, _(L"%ls: Unknown error"), L"set" );
+ retcode=1;
+ break;
+ }
+ }
+
+ sb_destroy( &sb );
+
+ return retcode;
+}
+
+/**
+ Extract indexes from a destination argument of the form name[index1 index2...]
+
+ \param indexes the list to insert the new indexes into
+ \param src the source string to parse
+ \param name the name of the element. Return null if the name in \c src does not match this name
+ \param var_count the number of elements in the array to parse.
+
+ \return the total number of indexes parsed, or -1 on error
+*/
+static int parse_index( array_list_t *indexes,
+ const wchar_t *src,
+ const wchar_t *name,
+ int var_count )
+{
+ int len;
+
+ int count = 0;
+ const wchar_t *src_orig = src;
+
+ if (src == 0)
+ {
+ return 0;
+ }
+
+ while (*src != L'\0' && (iswalnum(*src) || *src == L'_'))
+ {
+ src++;
+ }
+
+ if (*src != L'[')
+ {
+ sb_printf( sb_err, _(BUILTIN_SET_ARG_COUNT), L"set" );
+ return 0;
+ }
+
+ len = src-src_orig;
+
+ if( (wcsncmp( src_orig, name, len )!=0) || (wcslen(name) != (len)) )
+ {
+ sb_printf( sb_err,
+ _(L"%ls: Multiple variable names specified in single call (%ls and %.*ls)\n"),
+ L"set",
+ name,
+ len,
+ src_orig);
+ return 0;
+ }
+
+ src++;
+
+ while (iswspace(*src))
+ {
+ src++;
+ }
+
+ while (*src != L']')
+ {
+ wchar_t *end;
+
+ long l_ind;
+
+ errno = 0;
+
+ l_ind = wcstol(src, &end, 10);
+
+ if( end==src || errno )
+ {
+ sb_printf(sb_err, _(L"%ls: Invalid index starting at '%ls'\n"), L"set", src);
+ return 0;
+ }
+
+ if( l_ind < 0 )
+ {
+ l_ind = var_count+l_ind+1;
+ }
+
+ al_push_long(indexes, l_ind);
+ src = end;
+ count++;
+ while (iswspace(*src)) src++;
+ }
+
+ return count;
+}
+
+
+/**
+ Update a list \c list by writing copies (using wcsdup) of the
+ values specified by \c values to the indexes specified by \c
+ indexes. The previous entries at the specidied position will be
+ free'd.
+
+ \return 0 if the operation was successfull, non-zero otherwise
+*/
+static int update_values( array_list_t *list,
+ array_list_t *indexes,
+ array_list_t *values )
+{
+ int i;
+
+ /* Replace values where needed */
+ for( i = 0; i < al_get_count(indexes); i++ )
+ {
+ /*
+ The '- 1' below is because the indices in fish are
+ one-based, but the array_list_t uses zero-based indices
+ */
+ long ind = al_get_long(indexes, i) - 1;
+ void *new = (void *) al_get(values, i);
+ if( ind < 0 )
+ {
+ return 1;
+ }
+
+ free((void *) al_get(list, ind));
+ al_set(list, ind, new != 0 ? wcsdup(new) : wcsdup(L""));
+ }
+
+ return 0;
+}
+
+
+/**
+ Return 1 if an array list of longs contains the specified
+ value, 0 otherwise
+*/
+static int al_contains_long( array_list_t *list,
+ long val)
+{
+ int i;
+
+ for (i = 0; i < al_get_count(list); i++)
+ {
+ long current = al_get_long(list, i);
+ if( current != 0 && current == val )
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ Erase from a list values at specified indexes
+*/
+static void erase_values(array_list_t *list, array_list_t *indexes)
+{
+ long i;
+ array_list_t result;
+
+ al_init(&result);
+
+ for (i = 0; i < al_get_count(list); i++)
+ {
+ if (!al_contains_long(indexes, i + 1))
+ {
+ al_push(&result, al_get(list, i));
+ }
+ else
+ {
+ free( (void *)al_get(list, i));
+ }
+ }
+
+ al_truncate(list,0);
+ al_push_all( list, &result );
+ al_destroy(&result);
+}
+
+
+/**
+ Print the names of all environment variables in the scope, with or without values,
+ with or without escaping
+*/
+static void print_variables(int include_values, int esc, int scope)
+{
+ array_list_t names;
+ int i;
+
+ al_init( &names );
+ env_get_names( &names, scope );
+
+ sort_list( &names );
+
+ for( i = 0; i < al_get_count(&names); i++ )
+ {
+ wchar_t *key = (wchar_t *)al_get( &names, i );
+ wchar_t *e_key = esc ? escape(key, 0) : wcsdup(key);
+
+ sb_append(sb_out, e_key);
+
+ if( include_values )
+ {
+ wchar_t *value = env_get(key);
+ wchar_t *e_value;
+ if( value )
+ {
+ int shorten = 0;
+
+ if( wcslen( value ) > 64 )
+ {
+ shorten = 1;
+ value = wcsndup( value, 60 );
+ if( !value )
+ {
+ DIE_MEM();
+ }
+ }
+
+ e_value = esc ? expand_escape_variable(value) : wcsdup(value);
+
+ sb_append(sb_out, L" ", e_value, (void *)0);
+ free(e_value);
+
+ if( shorten )
+ {
+ sb_append(sb_out, L"\u2026");
+ free( value );
+ }
+
+ }
+ }
+
+ sb_append(sb_out, L"\n");
+ free(e_key);
+ }
+ al_destroy(&names);
+}
+
+
+
+/**
+ The set builtin. Creates, updates and erases environment variables
+ and environemnt variable arrays.
+*/
+static int builtin_set( wchar_t **argv )
+{
+
+ /**
+ Variables used for parsing the argument list
+ */
+ static const struct woption
+ long_options[] =
+ {
+ {
+ L"export", no_argument, 0, 'x'
+ }
+ ,
+ {
+ L"global", no_argument, 0, 'g'
+ }
+ ,
+ {
+ L"local", no_argument, 0, 'l'
+ }
+ ,
+ {
+ L"erase", no_argument, 0, 'e'
+ }
+ ,
+ {
+ L"names", no_argument, 0, 'n'
+ }
+ ,
+ {
+ L"unexport", no_argument, 0, 'u'
+ }
+ ,
+ {
+ L"universal", no_argument, 0, 'U'
+ }
+ ,
+ {
+ L"query", no_argument, 0, 'q'
+ }
+ ,
+ {
+ L"help", no_argument, 0, 'h'
+ }
+ ,
+ {
+ 0, 0, 0, 0
+ }
+ }
+ ;
+
+ const wchar_t *short_options = L"+xglenuUqh";
+
+ int argc = builtin_count_args(argv);
+
+ /*
+ Flags to set the work mode
+ */
+ int local = 0, global = 0, export = 0;
+ int erase = 0, list = 0, unexport=0;
+ int universal = 0, query=0;
+
+
+ /*
+ Variables used for performing the actual work
+ */
+ wchar_t *dest = 0;
+ int retcode=0;
+ int scope;
+ int slice=0;
+ int i;
+
+ wchar_t *bad_char;
+
+
+ /* Parse options to obtain the requested operation and the modifiers */
+ woptind = 0;
+ while (1)
+ {
+ int c = wgetopt_long(argc, argv, short_options, long_options, 0);
+
+ if (c == -1)
+ {
+ break;
+ }
+
+ switch(c)
+ {
+ case 0:
+ break;
+
+ case 'e':
+ erase = 1;
+ break;
+
+ case 'n':
+ list = 1;
+ break;
+
+ case 'x':
+ export = 1;
+ break;
+
+ case 'l':
+ local = 1;
+ break;
+
+ case 'g':
+ global = 1;
+ break;
+
+ case 'u':
+ unexport = 1;
+ break;
+
+ case 'U':
+ universal = 1;
+ break;
+
+ case 'q':
+ query = 1;
+ break;
+
+ case 'h':
+ builtin_print_help( argv[0], sb_out );
+ return 0;
+
+ case '?':
+ builtin_unknown_option( argv[0], argv[woptind-1] );
+ return 1;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ Ok, all arguments have been parsed, let's validate them
+ */
+
+ /*
+ If we are checking the existance of a variable (-q) we can not
+ also specify scope
+ */
+
+ if( query && (erase || list || global || local || universal || export || unexport ) )
+ {
+ sb_printf(sb_err,
+ BUILTIN_ERR_COMBO,
+ argv[0] );
+
+ builtin_print_help( argv[0], sb_err );
+ return 1;
+ }
+
+
+ /* We can't both list and erase varaibles */
+ if( erase && list )
+ {
+ sb_printf(sb_err,
+ BUILTIN_ERR_COMBO,
+ argv[0] );
+
+ builtin_print_help( argv[0], sb_err );
+ return 1;
+ }
+
+ /*
+ Variables can only have one scope
+ */
+ if( local + global + universal > 1 )
+ {
+ sb_printf( sb_err,
+ BUILTIN_ERR_GLOCAL,
+ argv[0] );
+ builtin_print_help( argv[0], sb_err );
+ return 1;
+ }
+
+ /*
+ Variables can only have one export status
+ */
+ if( export && unexport )
+ {
+ sb_printf( sb_err,
+ BUILTIN_ERR_EXPUNEXP,
+ argv[0] );
+ builtin_print_help( argv[0], sb_err );
+ return 1;
+ }
+
+ /*
+ Calculate the scope value for variable assignement
+ */
+ scope = (local ? ENV_LOCAL : 0) | (global ? ENV_GLOBAL : 0) | (export ? ENV_EXPORT : 0) | (unexport ? ENV_UNEXPORT : 0) | (universal ? ENV_UNIVERSAL:0) | ENV_USER;
+
+ if( query )
+ {
+ /*
+ Query mode. Return the number of variables that do not exist
+ out of the specified variables.
+ */
+ int i;
+ for( i=woptind; i<argc; i++ )
+ {
+ wchar_t *arg = argv[i];
+ int slice=0;
+
+ if( !(dest = wcsdup(arg)))
+ {
+ DIE_MEM();
+ }
+
+ if( wcschr( dest, L'[' ) )
+ {
+ slice = 1;
+ *wcschr( dest, L'[' )=0;
+ }
+
+ if( slice )
+ {
+ array_list_t indexes;
+ array_list_t result;
+ int j;
+
+ al_init( &result );
+ al_init( &indexes );
+
+ tokenize_variable_array( env_get( dest ), &result );
+
+ if( !parse_index( &indexes, arg, dest, al_get_count( &result ) ) )
+ {
+ builtin_print_help( argv[0], sb_err );
+ retcode = 1;
+ break;
+ }
+ for( j=0; j<al_get_count( &indexes ); j++ )
+ {
+ long idx = al_get_long( &indexes, j );
+ if( idx < 1 || idx > al_get_count( &result ) )
+ {
+ retcode++;
+ }
+ }
+ }
+ else
+ {
+ if( !env_exist( arg, scope ) )
+ {
+ retcode++;
+ }
+ }
+
+ free( dest );
+
+ }
+ return retcode;
+ }
+
+ if( list )
+ {
+ /* Maybe we should issue an error if there are any other arguments? */
+ print_variables(0, 0, scope);
+ return 0;
+ }
+
+ if( woptind == argc )
+ {
+ /*
+ Print values of variables
+ */
+
+ if( erase )
+ {
+ sb_printf( sb_err,
+ _(L"%ls: Erase needs a variable name\n%ls\n"),
+ argv[0] );
+
+ builtin_print_help( argv[0], sb_err );
+ retcode = 1;
+ }
+ else
+ {
+ print_variables( 1, 1, scope );
+ }
+
+ return retcode;
+ }
+
+ if( !(dest = wcsdup(argv[woptind])))
+ {
+ DIE_MEM();
+ }
+
+ if( wcschr( dest, L'[' ) )
+ {
+ slice = 1;
+ *wcschr( dest, L'[' )=0;
+ }
+
+ if( !wcslen( dest ) )
+ {
+ free( dest );
+ sb_printf( sb_err, BUILTIN_ERR_VARNAME_ZERO, argv[0] );
+ builtin_print_help( argv[0], sb_err );
+ return 1;
+ }
+
+ if( (bad_char = wcsvarname( dest ) ) )
+ {
+ sb_printf( sb_err, BUILTIN_ERR_VARCHAR, argv[0], *bad_char );
+ builtin_print_help( argv[0], sb_err );
+ free( dest );
+ return 1;
+ }
+
+ if( slice && erase && (scope != ENV_USER) )
+ {
+ free( dest );
+ sb_printf( sb_err, _(L"%ls: Can not specify scope when erasing array slice\n"), argv[0] );
+ builtin_print_help( argv[0], sb_err );
+ return 1;
+ }
+
+ /*
+ set assignment can work in two modes, either using slices or
+ using the whole array. We detect which mode is used here.
+ */
+
+ if( slice )
+ {
+
+ /*
+ Slice mode
+ */
+ int idx_count, val_count;
+ array_list_t values;
+ array_list_t indexes;
+ array_list_t result;
+
+ al_init(&values);
+ al_init(&indexes);
+ al_init(&result);
+
+ tokenize_variable_array( env_get(dest), &result );
+
+ for( ; woptind<argc; woptind++ )
+ {
+ if( !parse_index( &indexes, argv[woptind], dest, al_get_count( &result ) ) )
+ {
+ builtin_print_help( argv[0], sb_err );
+ retcode = 1;
+ break;
+ }
+
+ val_count = argc-woptind-1;
+ idx_count = al_get_count( &indexes );
+
+ if( !erase )
+ {
+ if( val_count < idx_count )
+ {
+ sb_printf( sb_err, _(BUILTIN_SET_ARG_COUNT), argv[0] );
+ builtin_print_help( argv[0], sb_err );
+ retcode=1;
+ break;
+ }
+ if( val_count == idx_count )
+ {
+ woptind++;
+ break;
+ }
+ }
+ }
+
+ if( !retcode )
+ {
+ /*
+ Slice indexes have been calculated, do the actual work
+ */
+
+ if( erase )
+ {
+ erase_values(&result, &indexes);
+ my_env_set( dest, &result, scope);
+ }
+ else
+ {
+ array_list_t value;
+ al_init(&value);
+
+ while( woptind < argc )
+ {
+ al_push(&value, argv[woptind++]);
+ }
+
+ if( update_values( &result,
+ &indexes,
+ &value ) )
+ {
+ sb_printf( sb_err, L"%ls: ", argv[0] );
+ sb_printf( sb_err, ARRAY_BOUNDS_ERR );
+ sb_append( sb_err, L"\n" );
+ }
+
+ my_env_set(dest,
+ &result,
+ scope);
+
+ al_destroy( &value );
+
+ }
+ }
+
+ al_foreach( &result, &free );
+ al_destroy( &result );
+
+ al_destroy(&indexes);
+ al_destroy(&values);
+
+ }
+ else
+ {
+ woptind++;
+
+ /*
+ No slicing
+ */
+ if( erase )
+ {
+ if( woptind != argc )
+ {
+ sb_printf( sb_err,
+ _(L"%ls: Values cannot be specfied with erase\n"),
+ argv[0] );
+ builtin_print_help( argv[0], sb_err );
+ retcode=1;
+ }
+ else
+ {
+ retcode = env_remove( dest, scope );
+ }
+ }
+ else
+ {
+ array_list_t val;
+ al_init( &val );
+
+ for( i=woptind; i<argc; i++ )
+ {
+ al_push( &val, argv[i] );
+ }
+
+ retcode = my_env_set( dest, &val, scope );
+
+ al_destroy( &val );
+
+ }
+ }
+
+ free( dest );
+
+ return retcode;
+
+}
+