/** \file builtin_set.c Functions defining the set builtin Functions used for implementing the set builtin. */ #include "config.h" #include #include #include #include #include #include #include #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_str( env, L"PATH", L"CDPATH", (void *)0 ); } /** 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 64 ) { shorten = 1; value = wcsndup( value, 60 ); if( !value ) { DIE_MEM(); } } e_value = esc ? expand_escape_variable(value) : wcsdup(value); sb_append2(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 */ const static 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 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