/************************************************ dict.c - $Author: matz $ $Date: 1995/01/10 10:42:26 $ created at: Mon Nov 22 18:51:18 JST 1993 Copyright (C) 1994 Yukihiro Matsumoto ************************************************/ #include "ruby.h" #include "st.h" VALUE C_Dict; static VALUE envtbl; static ID hash; VALUE Fgetenv(), Fsetenv(); static VALUE rb_cmp(a, b) VALUE a, b; { return rb_equal(a, b)?0:1; } static VALUE rb_hash(a, mod) VALUE a; int mod; { return rb_funcall(a, hash, 0) % mod; } #define ASSOC_KEY(a) RCONS(a)->car #define ASSOC_VAL(a) RCONS(a)->cdr static VALUE Sdic_new(class) VALUE class; { NEWOBJ(dic, struct RDict); OBJSETUP(dic, class, T_DICT); dic->tbl = st_init_table(rb_cmp, rb_hash); return (VALUE)dic; } static VALUE Fdic_clone(); static VALUE Sdic_create(argc, argv, class) int argc; VALUE *argv; VALUE class; { struct RDict *dic; int i; if (argc == 1 && TYPE(argv[0]) == T_DICT) { if (class == CLASS_OF(argv[0])) return argv[0]; else { NEWOBJ(dic, struct RDict); OBJSETUP(dic, class, T_DICT); dic->tbl = (st_table*)st_copy(RDICT(argv[0])->tbl); return (VALUE)dic; } } if (argc % 2 != 0) { Fail("odd number args for Dict"); } dic = (struct RDict*)Sdic_new(class); for (i=0; itbl, argv[i], argv[i+1]); } return (VALUE)dic; } VALUE dic_new() { return Sdic_new(C_Dict); } static VALUE Fdic_clone(dic) struct RDict *dic; { NEWOBJ(dic2, struct RDict); CLONESETUP(dic2, dic); dic2->tbl = (st_table*)st_copy(dic->tbl); return (VALUE)dic2; } static VALUE Fdic_aref(dic, key) struct RDict *dic; VALUE key; { VALUE val = Qnil; if (!st_lookup(dic->tbl, key, &val)) { return Qnil; } return val; } static VALUE Fdic_indexes(dic, args) struct RDict *dic; struct RArray *args; { VALUE *p, *pend; struct RArray *new; int i = 0; if (!args || args->len == 0) { Fail("wrong # of argment"); } else if (args->len == 1) { if (TYPE(args->ptr[0])) { args = (struct RArray*)rb_to_a(args->ptr[0]); } else { args = (struct RArray*)args->ptr[0]; } } new = (struct RArray*)ary_new2(args->len); p = args->ptr; pend = p + args->len; while (p < pend) { new->ptr[i++] = Fdic_aref(dic, *p++); } new->len = i; return (VALUE)new; } static VALUE Fdic_delete(dic, key) struct RDict *dic; VALUE key; { VALUE val; if (st_delete(dic->tbl, &key, &val)) return val; return Qnil; } static int dic_delete_if(key, value) VALUE key, value; { if (rb_yield(assoc_new(key, value))) return ST_DELETE; return ST_CONTINUE; } static VALUE Fdic_delete_if(dic) struct RDict *dic; { st_foreach(dic->tbl, dic_delete_if, Qnil); return (VALUE)dic; } static dic_clear(key, value) VALUE key, value; { return ST_DELETE; } static VALUE Fdic_clear(dic) struct RDict *dic; { st_foreach(dic->tbl, dic_clear, Qnil); return (VALUE)dic; } VALUE Fdic_aset(dic, key, val) struct RDict *dic; VALUE key, val; { if (val == Qnil) { Fdic_delete(dic, key); return Qnil; } st_insert(dic->tbl, key, val); return val; } static VALUE Fdic_length(dic) struct RDict *dic; { return INT2FIX(dic->tbl->num_entries); } static dic_each_value(key, value) VALUE key, value; { rb_yield(value); return ST_CONTINUE; } static VALUE Fdic_each_value(dic) struct RDict *dic; { st_foreach(dic->tbl, dic_each_value); return (VALUE)dic; } static dic_each_key(key, value) VALUE key, value; { rb_yield(key); return ST_CONTINUE; } static VALUE Fdic_each_key(dic) struct RDict *dic; { st_foreach(dic->tbl, dic_each_key); return (VALUE)dic; } static dic_each_pair(key, value) VALUE key, value; { rb_yield(assoc_new(key, value)); return ST_CONTINUE; } static VALUE Fdic_each_pair(dic) struct RDict *dic; { st_foreach(dic->tbl, dic_each_pair); return (VALUE)dic; } static dic_to_a(key, value, ary) VALUE key, value, ary; { ary_push(ary, assoc_new(key, value)); return ST_CONTINUE; } static VALUE Fdic_to_a(dic) struct RDict *dic; { VALUE ary; ary = ary_new(); st_foreach(dic->tbl, dic_to_a, ary); return ary; } static dic_inspect(key, value, str) VALUE key, value; struct RString *str; { VALUE str2; ID inspect = rb_intern("_inspect"); if (str->len > 1) { str_cat(str, ", ", 2); } str2 = rb_funcall(key, inspect, 0, Qnil); str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); str_cat(str, "=>", 2); str2 = rb_funcall(value, inspect, 0, Qnil); str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len); return ST_CONTINUE; } static VALUE Fdic_inspect(dic) struct RDict *dic; { VALUE str; str = str_new2("{"); st_foreach(dic->tbl, dic_inspect, str); str_cat(str, "}", 1); return str; } static VALUE Fdic_to_s(dic) VALUE dic; { return Fary_to_s(Fdic_to_a(dic)); } static dic_keys(key, value, ary) VALUE key, value, ary; { ary_push(ary, key); return ST_CONTINUE; } static VALUE Fdic_keys(dic) struct RDict *dic; { VALUE ary; ary = ary_new(); st_foreach(dic->tbl, dic_keys, ary); return ary; } static dic_values(key, value, ary) VALUE key, value, ary; { ary_push(ary, key); return ST_CONTINUE; } static VALUE Fdic_values(dic) struct RDict *dic; { VALUE ary; ary = ary_new(); st_foreach(dic->tbl, dic_values, ary); return ary; } static VALUE Fdic_has_key(dic, key) struct RDict *dic; VALUE key; { VALUE val; if (st_lookup(dic->tbl, key, &val)) return TRUE; return FALSE; } static int dic_search_value(key, value, data) VALUE key, value, *data; { if (rb_equal(value, data[1])) { data[0] = TRUE; return ST_STOP; } return ST_CONTINUE; } static VALUE Fdic_has_value(dic, val) struct RDict *dic; VALUE val; { VALUE data[2]; data[0] = FALSE; data[1] = val; st_foreach(dic->tbl, dic_search_value, data); return data[0]; } struct equal_data { int result; st_table *tbl; }; static int dic_equal(key, val1, data) VALUE key, val1; struct equal_data *data; { VALUE val2; if (!st_lookup(data->tbl, key, &val2)) { data->result = FALSE; return ST_STOP; } if (!rb_equal(val1, val2)) { data->result = FALSE; return ST_STOP; } return ST_CONTINUE; } static VALUE Fdic_equal(dic1, dic2) struct RDict *dic1, *dic2; { struct equal_data data; if (TYPE(dic2) != T_DICT) return FALSE; if (dic1->tbl->num_entries != dic2->tbl->num_entries) return FALSE; data.tbl = dic2->tbl; data.result = TRUE; st_foreach(dic1->tbl, dic_equal, &data); return data.result; } static int dic_hash(key, val, data) VALUE key, val; int *data; { *data ^= rb_funcall(key, hash, 0); *data ^= rb_funcall(val, hash, 0); return ST_CONTINUE; } static VALUE Fdic_hash(dic) struct RDict *dic; { int h; st_foreach(dic->tbl, dic_hash, &h); return INT2FIX(h); } char *strchr(); extern VALUE rb_readonly_hook(); extern char **environ; static VALUE Fenv_each(dic) VALUE dic; { char **env; env = environ; while (*env) { VALUE var, val; char *s = strchr(*env, '='); var = str_new(*env, s-*env); val = str_new2(s+1); rb_yield(assoc_new(var, val)); env++; } return dic; } static VALUE Fenv_delete(obj, name) VALUE obj; struct RString *name; { int i, len; char *nam, *val = Qnil; Check_Type(name, T_STRING); nam = name->ptr; len = strlen(nam); for(i=0; environ[i]; i++) { if (strncmp(environ[i], nam, len) == 0 && environ[i][len] == '=') { val = environ[i]+len+1; break; } } while (environ[i]) { environ[i] = environ[i+1]; i++; } if (val) { return str_new2(val); } return Qnil; } VALUE Fgetenv(obj, name) VALUE obj; struct RString *name; { extern char *getenv(); char *env; Check_Type(name, T_STRING); if (strlen(name->ptr) != name->len) Fail("Bad environment name"); env = getenv(name->ptr); if (env) { return str_new2(env); } return Qnil; } VALUE Fsetenv(obj, name, value) VALUE obj; struct RString *name, *value; { Check_Type(name, T_STRING); if (value == Qnil) { Fenv_delete(obj, name); return Qnil; } Check_Type(value, T_STRING); if (strlen(name->ptr) != name->len) Fail("Bad environment name"); if (strlen(value->ptr) != value->len) Fail("Bad environment value"); #ifdef HAVE_SETENV if (setenv(name->ptr, value->ptr, 1) == 0) return TRUE; #else #ifdef HAVE_PUTENV { char *str; int len; str = ALLOC_N(char, name->len + value->len + 2); sprintf("%s=%s", name->ptr, value->ptr); if (putenv(str) == 0) return TRUE; } #else Fail("setenv is not supported on this system"); #endif #endif Fail("setenv failed"); return FALSE; /* not reached */ } static VALUE Fenv_to_s() { return str_new2("$ENV"); } Init_Dict() { extern VALUE C_Kernel; extern VALUE M_Enumerable; hash = rb_intern("hash"); C_Dict = rb_define_class("Dict", C_Object); rb_name_class(C_Dict, rb_intern("Hash")); /* alias */ rb_include_module(C_Dict, M_Enumerable); rb_define_single_method(C_Dict, "new", Sdic_new, 0); rb_define_single_method(C_Dict, "[]", Sdic_create, -1); rb_define_method(C_Dict,"clone", Fdic_clone, 0); rb_define_method(C_Dict,"to_a", Fdic_to_a, 0); rb_define_method(C_Dict,"to_s", Fdic_to_s, 0); rb_define_method(C_Dict,"_inspect", Fdic_inspect, 0); rb_define_method(C_Dict,"==", Fdic_equal, 1); rb_define_method(C_Dict,"hash", Fdic_hash, 0); rb_define_method(C_Dict,"[]", Fdic_aref, 1); rb_define_method(C_Dict,"[]=", Fdic_aset, 2); rb_define_method(C_Dict,"indexes", Fdic_indexes, -2); rb_define_method(C_Dict,"length", Fdic_length, 0); rb_define_alias(C_Dict, "size", "length"); rb_define_method(C_Dict,"each", Fdic_each_pair, 0); rb_define_method(C_Dict,"each_value", Fdic_each_value, 0); rb_define_method(C_Dict,"each_key", Fdic_each_key, 0); rb_define_method(C_Dict,"each_pair", Fdic_each_pair, 0); rb_define_method(C_Dict,"keys", Fdic_keys, 0); rb_define_method(C_Dict,"values", Fdic_values, 0); rb_define_method(C_Dict,"delete", Fdic_delete, 1); rb_define_method(C_Dict,"delete_if", Fdic_delete_if, 0); rb_define_method(C_Dict,"clear", Fdic_clear, 0); rb_define_method(C_Dict,"includes", Fdic_has_key, 1); rb_define_method(C_Dict,"has_key", Fdic_has_key, 1); rb_define_method(C_Dict,"has_value", Fdic_has_value, 1); envtbl = obj_alloc(C_Object); rb_define_single_method(envtbl,"[]", Fgetenv, 1); rb_define_single_method(envtbl,"[]=", Fsetenv, 2); rb_define_single_method(envtbl,"each", Fenv_each, 0); rb_define_single_method(envtbl,"delete", Fenv_delete, 1); rb_define_single_method(envtbl,"to_s", Fenv_to_s, 0); rb_include_module(CLASS_OF(envtbl), M_Enumerable); rb_define_variable("$ENV", &envtbl, Qnil, rb_readonly_hook, 0); rb_define_private_method(C_Kernel, "getenv", Fgetenv, 1); rb_define_private_method(C_Kernel, "setenv", Fsetenv, 2); }