/** \file builtin_set.c Functions defining the set builtin Functions used for implementing the set builtin. */ #include #include #include #include #include #include #include #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; }