diff options
Diffstat (limited to 'builtin_set.c')
-rw-r--r-- | builtin_set.c | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/builtin_set.c b/builtin_set.c new file mode 100644 index 00000000..e47ea35d --- /dev/null +++ b/builtin_set.c @@ -0,0 +1,585 @@ +/** \file builtin_set.c Functions defining the set builtin + +Functions used for implementing the set builtin. + +*/ +#include <stdlib.h> +#include <stdio.h> +#include <wchar.h> +#include <wctype.h> +#include <sys/types.h> +#include <termios.h> +#include <signal.h> + +#include "config.h" +#include "util.h" +#include "builtin.h" +#include "env.h" +#include "expand.h" +#include "common.h" +#include "wgetopt.h" +#include "proc.h" +#include "parser.h" + +/** + Extract the name from a destination argument of the form name[index1 index2...] +*/ +static int parse_fill_name( string_buffer_t *name, + const wchar_t *src) +{ + + if (src == 0) + { + return 0; + } + + while (iswalnum(*src) || *src == L'_') + { + sb_append_char(name, *src++); + } + + if (*src != L'[' && *src != L'\0') + { + + sb_append(sb_err, L"set: Invalid character in variable name: "); + sb_append_char(sb_err, *src); + sb_append2(sb_err, L"\n", parser_current_line(), L"\n", 0 ); +// builtin_print_help( L"set", sb_err ); + + return -1; + } + else + { + return 0; + } +} + + +/** + Extract indexes from a destination argument of the form name[index1 index2...] +*/ +static int parse_fill_indexes( array_list_t *indexes, + const wchar_t *src) +{ + int count = 0; + + if (src == 0) + { + return 0; + } + + while (*src != L'\0' && (iswalnum(*src) || *src == L'_')) + { + src++; + } + + if (*src == L'\0') + { + return 0; + } + + if (*src++ != L'[') + { + return -1; + } + + while (iswblank(*src)) + { + src++; + } + + while (*src != L']') + { + wchar_t *end; + long l_ind = wcstol(src, &end, 10); + if (end == src) + { + wchar_t sbuf[256]; + swprintf(sbuf, 255, L"Invalid index starting at %ls\n", src); + sb_append(sb_err, sbuf); + return -1; + } + + int *ind = (int *) calloc(1, sizeof(int)); + *ind = (int) l_ind; + al_push(indexes, ind); + src = end; + count++; + while (iswblank(*src)) src++; + } + + return count; +} + + +/** + Update a list by writing the specified values at the specified indexes +*/ +static int update_values( array_list_t *list, + array_list_t *indexes, + array_list_t *values ) +{ + int i; + + //fwprintf(stderr, L"Scan complete\n"); + /* Replace values where needed */ + for( i = 0; i < al_get_count(indexes); i++ ) + { + int ind = *(int *) al_get(indexes, i) - 1; + void *new = (void *) al_get(values, i); + if (al_get(list, ind) != 0) + { + free((void *) al_get(list, ind)); + } + al_set(list, ind, new != 0 ? wcsdup(new) : wcsdup(L"")); + } + + return al_get_count(list); +} + + +/** + Return 1 if an array list of int* pointers contains the specified + value, 0 otherwise +*/ +static int al_contains_int( array_list_t *list, + int val) +{ + int i; + + for (i = 0; i < al_get_count(list); i++) + { + int *current = (int *) al_get(list, i); + if (current != 0 && *current == val) + { + return 1; + } + } + + return 0; +} + + +/** + Erase from a list values at specified indexes +*/ +static int erase_values(array_list_t *list, array_list_t *indexes) +{ + int i; + array_list_t result; + + //fwprintf(stderr, L"Starting with %d\n", al_get_count(list)); + + al_init(&result); + + for (i = 0; i < al_get_count(list); i++) + { + if (!al_contains_int(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); + + //fwprintf(stderr, L"Remaining: %d\n", al_get_count(&result)); + + return al_get_count(list); +} + + +/** + Fill a string buffer with values from a list, using ARRAY_SEP_STR to separate them +*/ +static int fill_buffer_from_list(string_buffer_t *sb, array_list_t *list) +{ + int i; + + for (i = 0; i < al_get_count(list); i++) + { + wchar_t *v = (wchar_t *) al_get(list, i); + if (v != 0) + { + // fwprintf(stderr, L".\n"); + // fwprintf(stderr, L"Collecting %ls from %d\n", v, i); + sb_append(sb, v); + } + if (i < al_get_count(list) - 1) + sb_append(sb, ARRAY_SEP_STR); + } + return al_get_count(list); +} + + +/** + 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 escape, int scope) +{ + array_list_t names; + wchar_t **names_arr; + int i; + + al_init( &names ); + env_get_names( &names, scope ); + names_arr = list_to_char_arr( &names ); + qsort( names_arr, + al_get_count( &names ), + sizeof(wchar_t *), + (int (*)(const void *, const void *))&wcsfilecmp ); + + for( i = 0; i < al_get_count(&names); i++ ) + { + wchar_t *key = names_arr[i]; + /* Why does expand_escape free its argument ?! */ + wchar_t *e_key = escape ? expand_escape(wcsdup(key), 1) : wcsdup(key); + sb_append(sb_out, e_key); + + if( include_values ) + { + wchar_t *value = env_get(key); + wchar_t *e_value = escape ? expand_escape_variable(value) : wcsdup(value); + sb_append2(sb_out, L" ", e_value, 0); + free(e_value); + } + + sb_append(sb_out, L"\n"); + free(e_key); + } + + free(names_arr); + al_destroy(&names); +} + +/** + The set builtin. Creates, updates and erases environment variables and environemnt variable arrays. +*/ + +int builtin_set( wchar_t **argv ) +{ + 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' + } , + { + 0, 0, 0, 0 + } + } + ; + + wchar_t short_options[] = L"xglenuU"; + + 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; + + + /* + Variables used for performing the actual work + */ + wchar_t *dest = 0; + array_list_t values; + string_buffer_t name_sb; + int retcode=0; + wchar_t *name; + array_list_t indexes; + int retval; + + + /* 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 '?': + return 1; + default: + break; + } + } + + /* Check operation and modifiers sanity */ + if( erase && list ) + { + sb_append2(sb_err, + argv[0], + BUILTIN_ERR_COMBO, + L"\n", + parser_current_line(), + L"\n", + 0); + builtin_print_help( argv[0], sb_err ); + return 1; + } + + if( local + global + universal > 1 ) + { + sb_printf( sb_err, + L"%ls%ls\n%ls\n", + argv[0], + BUILTIN_ERR_GLOCAL, + parser_current_line() ); + builtin_print_help( argv[0], sb_err ); + return 1; + } + + if( export && unexport ) + { + sb_append2(sb_err, + argv[0], + BUILTIN_ERR_EXPUNEXP, + L"\n", + parser_current_line(), + L"\n", + 0); + builtin_print_help( argv[0], sb_err ); + return 1; + } + + /* Parse destination */ + if( woptind < argc ) + { + dest = wcsdup(argv[woptind++]); + //fwprintf(stderr, L"Dest: %ls\n", dest); + } + + /* Parse values */ + // wchar_t **values = woptind < argc ? (wchar_t **) calloc(argc - woptind, sizeof(wchar_t *)) : 0; + + al_init(&values); + while( woptind < argc ) + { + al_push(&values, argv[woptind++]); + // fwprintf(stderr, L"Val: %ls\n", argv[woptind - 1]); + } + + /* Extract variable name and indexes */ + + sb_init(&name_sb); + retval = parse_fill_name(&name_sb, dest); + + + if( retval < 0 ) + retcode=1; + + if( !retcode ) + { + name = (wchar_t *) name_sb.buff; + //fwprintf(stderr, L"Name is %ls\n", name); + + al_init(&indexes); + retval = parse_fill_indexes(&indexes, dest); + if (retval < 0) + retcode = 1; + } + + if( !retcode ) + { + + int i; + int finished=0; + + /* Do the actual work */ + int scope = (local ? ENV_LOCAL : 0) | (global ? ENV_GLOBAL : 0) | (export ? ENV_EXPORT : 0) | (unexport ? ENV_UNEXPORT : 0) | (universal ? ENV_UNIVERSAL:0) | ENV_USER; + if( list ) + { + /* Maybe we should issue an error if there are any other arguments */ + print_variables(0, 0, scope); + finished=1; + } + + if( (!finished ) && + (name == 0 || wcslen(name) == 0)) + { + /* No arguments -- display name & value for all variables in scope */ + if( erase ) + { + sb_append2( sb_err, + argv[0], + L": Erase needs a variable name\n", + parser_current_line(), + L"\n", + 0 ); + builtin_print_help( argv[0], sb_err ); + retcode = 1; + } + else + { + print_variables( 1, 1, scope ); + } + + finished=1; + } + + + if( !finished ) + { + if( al_get_count( &values ) == 0 && + al_get_count( &indexes ) == 0 && + !erase && + !list ) + { + /* + Only update the variable scope + */ + env_set( name, 0, scope ); + finished = 1; + } + } + + if( !finished ) + { + /* There are some arguments, we have at least a variable name */ + if( erase && al_get_count(&values) != 0 ) + { + sb_append2( sb_err, + argv[0], + L": Values cannot be specfied with erase\n", + parser_current_line(), + L"\n", + 0 ); + builtin_print_help( argv[0], sb_err ); + retcode = 1; + } + else + { + /* All ok, we can alter the specified variable */ + array_list_t val_l; + al_init(&val_l); + + void *old=0; + + if (al_get_count(&indexes) == 0) + { + /* We will act upon the entire variable */ + + al_push( &val_l, wcsdup(L"") ); + old = val_l.arr; + + /* Build indexes for all variable or all new values */ + int end_index = erase ? al_get_count(&val_l) : al_get_count(&values); + for (i = 0; i < end_index; i++) + { + int *ind = (int *) calloc(1, sizeof(int)); + *ind = i + 1; + al_push(&indexes, ind); + } + } + else + { + /* We will act upon some specific indexes */ + expand_variable_array( env_get(name), &val_l ); + } + + string_buffer_t result_sb; + sb_init(&result_sb); + if (erase) + { + int rem = erase_values(&val_l, &indexes); + if (rem == 0) + { + env_remove(name, ENV_USER); + } + else + { + fill_buffer_from_list(&result_sb, &val_l); + env_set(name, (wchar_t *) result_sb.buff, scope); + } + } + else + { + + update_values( &val_l, + &indexes, + &values ); + + fill_buffer_from_list( &result_sb, + &val_l ); + + env_set(name, + (wchar_t *) result_sb.buff, + scope); + } + + al_foreach( &val_l, (void (*)(const void *))&free ); + al_destroy(&val_l); + sb_destroy(&result_sb); + } + } + + al_foreach( &indexes, (void (*)(const void *))&free ); + al_destroy(&indexes); + } + +/* Common cleanup */ +//fwprintf(stderr, L"Cleanup\n"); + free(dest); + sb_destroy(&name_sb); + al_destroy( &values ); + + return retcode; +} + |