Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 742 lines (664 sloc) 16.405 kB
208fcab moved the ENV stuff into a separate file
Laurent Sansonetti authored
1 /*
2 * MacRuby implementation of ENV.
3 *
4 * This file is covered by the Ruby license. See COPYING for more details.
5 *
6 * Copyright (C) 2007-2010, Apple Inc. All rights reserved.
7 * Copyright (C) 1993-2007 Yukihiro Matsumoto
8 * Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
9 * Copyright (C) 2000 Information-technology Promotion Agency, Japan
10 */
11
468a2ea Move Obj-C related headers around.
Thibault Martin-Lagardette authored
12 #include "ruby/macruby.h"
208fcab moved the ENV stuff into a separate file
Laurent Sansonetti authored
13 #include "ruby/st.h"
14 #include "ruby/util.h"
15 #include "ruby/node.h"
16 #include "vm.h"
17
59746d9 cleaning up a few things
Laurent Sansonetti authored
18 char ***_NSGetEnviron();
208fcab moved the ENV stuff into a separate file
Laurent Sansonetti authored
19
20 static VALUE envtbl;
21
22 static int path_tainted = -1;
23
24 static char **origenviron;
25 #define GET_ENVIRON() (*_NSGetEnviron())
26
27 static VALUE
28 to_hash(VALUE hash)
29 {
30 return rb_convert_type(hash, T_HASH, "Hash", "to_hash");
31 }
32
33 static VALUE
34 env_str_new(const char *ptr, long len)
35 {
36 VALUE str = rb_tainted_str_new(ptr, len);
37 rb_obj_freeze(str);
38 return str;
39 }
40
41 static VALUE
42 env_str_new2(const char *ptr)
43 {
44 if (ptr == NULL) {
45 return Qnil;
46 }
47 return env_str_new(ptr, strlen(ptr));
48 }
49
50 static VALUE
51 env_delete(VALUE obj, VALUE name)
52 {
53 rb_secure(4);
54 SafeStringValue(name);
55 const char *nam = RSTRING_PTR(name);
56 if (strlen(nam) != RSTRING_LEN(name)) {
57 rb_raise(rb_eArgError, "bad environment variable name");
58 }
59 const char *val = getenv(nam);
60 if (val != NULL) {
61 VALUE value = env_str_new2(val);
62 ruby_setenv(nam, 0);
63 if (strcmp(nam, PATH_ENV) == 0) {
64 path_tainted = 0;
65 }
66 return value;
67 }
68 return Qnil;
69 }
70
71 static VALUE
72 env_delete_m(VALUE obj, SEL sel, VALUE name)
73 {
74 VALUE val = env_delete(obj, name);
75 if (NIL_P(val) && rb_block_given_p()) {
76 rb_yield(name);
77 }
78 return val;
79 }
80
81 static VALUE
82 rb_f_getenv(VALUE obj, SEL sel, VALUE name)
83 {
84 rb_secure(4);
85 SafeStringValue(name);
86 const char *nam = RSTRING_PTR(name);
87 if (strlen(nam) != RSTRING_LEN(name)) {
88 rb_raise(rb_eArgError, "bad environment variable name");
89 }
90 const char *env = getenv(nam);
91 if (env != NULL) {
92 if (strcmp(nam, PATH_ENV) == 0 && !rb_env_path_tainted()) {
93 VALUE str = rb_str_new2(env);
94 rb_obj_freeze(str);
95 return str;
96 }
97 return env_str_new2(env);
98 }
99 return Qnil;
100 }
101
102 static VALUE
103 env_fetch(VALUE rcv, SEL sel, int argc, VALUE *argv)
104 {
105 rb_secure(4);
106
107 VALUE key, if_none;
108 rb_scan_args(argc, argv, "11", &key, &if_none);
109
110 const bool block_given = rb_block_given_p();
111 if (block_given && argc == 2) {
112 rb_warn("block supersedes default value argument");
113 }
114 SafeStringValue(key);
115 const char *nam = RSTRING_PTR(key);
116 if (strlen(nam) != RSTRING_LEN(key)) {
117 rb_raise(rb_eArgError, "bad environment variable name");
118 }
119 const char *env = getenv(nam);
120 if (env == NULL) {
121 if (block_given) {
122 return rb_yield(key);
123 }
124 if (argc == 1) {
125 rb_raise(rb_eKeyError, "key not found");
126 }
127 return if_none;
128 }
129 if (strcmp(nam, PATH_ENV) == 0 && !rb_env_path_tainted()) {
130 return rb_str_new2(env);
131 }
132 return env_str_new2(env);
133 }
134
135 static void
136 path_tainted_p(const char *path)
137 {
138 path_tainted = rb_path_check(path) ? 0 : 1;
139 }
140
141 int
142 rb_env_path_tainted(void)
143 {
144 if (path_tainted < 0) {
145 path_tainted_p(getenv(PATH_ENV));
146 }
147 return path_tainted;
148 }
149
150 void
151 ruby_setenv(const char *name, const char *value)
152 {
153 #undef setenv
154 #undef unsetenv
155 if (value != NULL) {
156 setenv(name, value, 1);
157 }
158 else {
159 unsetenv(name);
160 }
161 }
162
163 void
164 ruby_unsetenv(const char *name)
165 {
166 ruby_setenv(name, 0);
167 }
168
169 static VALUE
170 env_aset(VALUE obj, SEL sel, VALUE nm, VALUE val)
171 {
172 if (rb_safe_level() >= 4) {
173 rb_raise(rb_eSecurityError, "can't change environment variable");
174 }
175
176 if (NIL_P(val)) {
177 env_delete(obj, nm);
178 return Qnil;
179 }
180 StringValue(nm);
181 StringValue(val);
182 const char *name = RSTRING_PTR(nm);
183 const char *value = RSTRING_PTR(val);
184 if (strlen(name) != RSTRING_LEN(nm)) {
185 rb_raise(rb_eArgError, "bad environment variable name");
186 }
187 if (strlen(value) != RSTRING_LEN(val)) {
188 rb_raise(rb_eArgError, "bad environment variable value");
189 }
190
191 ruby_setenv(name, value);
192 if (strcmp(name, PATH_ENV) == 0) {
193 if (OBJ_TAINTED(val)) {
194 /* already tainted, no check */
195 path_tainted = 1;
196 return val;
197 }
198 else {
199 path_tainted_p(value);
200 }
201 }
202 return val;
203 }
204
205 static VALUE
206 env_keys(VALUE rcv, SEL sel)
207 {
208 rb_secure(4);
209
210 VALUE ary = rb_ary_new();
211 char **env = GET_ENVIRON();
212 while (*env != NULL) {
213 const char *s = strchr(*env, '=');
214 if (s != NULL) {
215 rb_ary_push(ary, env_str_new(*env, s - *env));
216 }
217 env++;
218 }
219 return ary;
220 }
221
222 static VALUE
223 env_each_key(VALUE ehash, SEL sel)
224 {
225 RETURN_ENUMERATOR(ehash, 0, 0);
226 VALUE keys = env_keys(Qnil, 0); /* rb_secure(4); */
227 for (long i = 0, count = RARRAY_LEN(keys); i < count; i++) {
228 rb_yield(RARRAY_AT(keys, i));
229 RETURN_IF_BROKEN();
230 }
231 return ehash;
232 }
233
234 static VALUE
235 env_values(VALUE rcv, SEL sel)
236 {
237 rb_secure(4);
238
239 VALUE ary = rb_ary_new();
240 char **env = GET_ENVIRON();
241 while (*env != NULL) {
242 const char *s = strchr(*env, '=');
243 if (s != NULL) {
244 rb_ary_push(ary, env_str_new2(s + 1));
245 }
246 env++;
247 }
248 return ary;
249 }
250
251 static VALUE
252 env_each_value(VALUE ehash, SEL sel)
253 {
254 RETURN_ENUMERATOR(ehash, 0, 0);
255 VALUE values = env_values(Qnil, 0); /* rb_secure(4); */
256 for (long i = 0, count = RARRAY_LEN(values); i < count; i++) {
257 rb_yield(RARRAY_AT(values, i));
258 RETURN_IF_BROKEN();
259 }
260 return ehash;
261 }
262
263 static VALUE
264 env_each_pair(VALUE ehash, SEL sel)
265 {
266 RETURN_ENUMERATOR(ehash, 0, 0);
267
268 rb_secure(4);
269 VALUE ary = rb_ary_new();
270 char **env = GET_ENVIRON();
271 while (*env != NULL) {
272 const char *s = strchr(*env, '=');
273 if (s != NULL) {
274 rb_ary_push(ary, env_str_new(*env, s - *env));
275 rb_ary_push(ary, env_str_new2(s + 1));
276 }
277 env++;
278 }
279
280 for (long i = 0, count = RARRAY_LEN(ary); i < count; i += 2) {
281 rb_yield(rb_assoc_new(RARRAY_AT(ary, i), RARRAY_AT(ary, i + 1)));
282 RETURN_IF_BROKEN();
283 }
284 return ehash;
285 }
286
287 static VALUE
288 env_reject_bang(VALUE ehash, SEL sel)
289 {
290 RETURN_ENUMERATOR(ehash, 0, 0);
291 VALUE keys = env_keys(Qnil, 0); /* rb_secure(4); */
292 bool deleted = false;
293 for (long i = 0, count = RARRAY_LEN(keys); i < count; i++) {
294 VALUE key = RARRAY_AT(keys, i);
295 VALUE val = rb_f_getenv(Qnil, 0, key);
296 if (!NIL_P(val)) {
297 VALUE v = rb_yield_values(2, key, val);
298 RETURN_IF_BROKEN();
299 if (RTEST(v)) {
300 rb_obj_untaint(key);
301 env_delete(Qnil, key);
302 deleted = true;
303 }
304 }
305 }
306 return deleted ? envtbl : Qnil;
307 }
308
309 static VALUE
310 env_delete_if(VALUE ehash, SEL sel)
311 {
312 RETURN_ENUMERATOR(ehash, 0, 0);
313 env_reject_bang(ehash, 0);
314 return envtbl;
315 }
316
317 static VALUE
318 env_values_at(VALUE rcv, SEL sel, int argc, VALUE *argv)
319 {
320 rb_secure(4);
321 VALUE result = rb_ary_new();
322 for (long i = 0; i < argc; i++) {
323 rb_ary_push(result, rb_f_getenv(Qnil, 0, argv[i]));
324 }
325 return result;
326 }
327
328 static VALUE
329 env_select(VALUE ehash, SEL sel)
330 {
331 RETURN_ENUMERATOR(ehash, 0, 0);
332 rb_secure(4);
333 VALUE result = rb_hash_new();
334 char **env = GET_ENVIRON();
335 while (*env != NULL) {
336 const char *s = strchr(*env, '=');
337 if (s != NULL) {
338 VALUE k = env_str_new(*env, s - *env);
339 VALUE v = env_str_new2(s + 1);
340 VALUE v2 = rb_yield_values(2, k, v);
341 RETURN_IF_BROKEN();
342 if (RTEST(v2)) {
343 rb_hash_aset(result, k, v);
344 }
345 }
346 env++;
347 }
348 return result;
349 }
350
351 static VALUE
352 rb_env_clear_imp(VALUE rcv, SEL sel)
353 {
354 VALUE keys = env_keys(Qnil, 0); /* rb_secure(4); */
355 for (long i = 0, count = RARRAY_LEN(keys); i < count; i++) {
356 VALUE val = rb_f_getenv(Qnil, 0, RARRAY_AT(keys, i));
357 if (!NIL_P(val)) {
358 env_delete(Qnil, RARRAY_AT(keys, i));
359 }
360 }
361 return envtbl;
362 }
363
364 VALUE
365 rb_env_clear(void)
366 {
367 return rb_env_clear_imp(Qnil, 0);
368 }
369
370 static VALUE
371 env_to_s(VALUE rcv, SEL sel)
372 {
373 return rb_usascii_str_new2("ENV");
374 }
375
376 static VALUE
377 env_inspect(VALUE rcv, SEL sel)
378 {
379 rb_secure(4);
380
381 VALUE str = rb_str_buf_new2("{");
382 char **env = GET_ENVIRON();
383 while (*env != NULL) {
384 const char *s = strchr(*env, '=');
385
386 if (env != GET_ENVIRON()) {
387 rb_str_buf_cat2(str, ", ");
388 }
389 if (s != NULL) {
390 rb_str_buf_cat2(str, "\"");
391 rb_str_buf_cat(str, *env, s - *env);
392 rb_str_buf_cat2(str, "\"=>");
393 VALUE i = rb_inspect(rb_str_new2(s + 1));
394 rb_str_buf_append(str, i);
395 }
396 env++;
397 }
398 rb_str_buf_cat2(str, "}");
399 OBJ_TAINT(str);
400
401 return str;
402 }
403
404 static VALUE
405 env_to_a(VALUE rcv, SEL sel)
406 {
407 rb_secure(4);
408
409 VALUE ary = rb_ary_new();
410 char **env = GET_ENVIRON();
411 while (*env != NULL) {
412 const char *s = strchr(*env, '=');
413 if (s != NULL) {
414 rb_ary_push(ary, rb_assoc_new(env_str_new(*env, s - *env),
415 env_str_new2(s + 1)));
416 }
417 env++;
418 }
419 return ary;
420 }
421
422 static VALUE
423 env_none(VALUE rcv, SEL sel)
424 {
425 return Qnil;
426 }
427
428 static VALUE
429 env_size(VALUE rcv, SEL sel)
430 {
431 rb_secure(4);
432
433 char **env = GET_ENVIRON();
434 int i = 0;
435 while (env[i] != NULL) {
436 i++;
437 }
438 return INT2FIX(i);
439 }
440
441 static VALUE
442 env_empty_p(VALUE rcv, SEL sel)
443 {
444 rb_secure(4);
445
446 char **env = GET_ENVIRON();
447 if (env[0] == NULL) {
448 return Qtrue;
449 }
450 return Qfalse;
451 }
452
453 static VALUE
454 env_has_key(VALUE env, SEL sel, VALUE key)
455 {
456 rb_secure(4);
457
458 const char *s = StringValuePtr(key);
459 if (strlen(s) != RSTRING_LEN(key)) {
460 rb_raise(rb_eArgError, "bad environment variable name");
461 }
462 if (getenv(s) != NULL) {
463 return Qtrue;
464 }
465 return Qfalse;
466 }
467
468 static VALUE
469 env_assoc(VALUE env, SEL sel, VALUE key)
470 {
471 rb_secure(4);
472
473 const char *s = StringValuePtr(key);
474 if (strlen(s) != RSTRING_LEN(key)) {
475 rb_raise(rb_eArgError, "bad environment variable name");
476 }
477 const char *e = getenv(s);
478 if (e != NULL) {
479 return rb_assoc_new(key, rb_tainted_str_new2(e));
480 }
481 return Qnil;
482 }
483
484 static VALUE
485 env_has_value(VALUE dmy, SEL sel, VALUE obj)
486 {
487 rb_secure(4);
488
489 obj = rb_check_string_type(obj);
490 if (NIL_P(obj)) {
491 return Qnil;
492 }
493 char **env = GET_ENVIRON();
494 while (*env != NULL) {
495 const char *s = strchr(*env, '=');
496 if (s++ != NULL) {
497 const long len = strlen(s);
498 if (RSTRING_LEN(obj) == len
499 && strncmp(s, RSTRING_PTR(obj), len) == 0) {
500 return Qtrue;
501 }
502 }
503 env++;
504 }
505 return Qfalse;
506 }
507
508 static VALUE
13f1b83 Add selector to the implementation of `ENV#rassoc`
Thibault Martin-Lagardette authored
509 env_rassoc(VALUE dmy, SEL sel, VALUE obj)
208fcab moved the ENV stuff into a separate file
Laurent Sansonetti authored
510 {
511 rb_secure(4);
512
513 obj = rb_check_string_type(obj);
514 if (NIL_P(obj)) {
515 return Qnil;
516 }
517 char **env = GET_ENVIRON();
518 while (*env != NULL) {
519 const char *s = strchr(*env, '=');
520 if (s++ != NULL) {
521 const long len = strlen(s);
522 if (RSTRING_LEN(obj) == len
523 && strncmp(s, RSTRING_PTR(obj), len) == 0) {
524 return rb_assoc_new(rb_tainted_str_new(*env, s - *env - 1),
525 obj);
526 }
527 }
528 env++;
529 }
530 return Qnil;
531 }
532
533 static VALUE
534 env_key(VALUE dmy, SEL sel, VALUE value)
535 {
536 rb_secure(4);
537
538 StringValue(value);
539 char **env = GET_ENVIRON();
540 while (*env != NULL) {
541 const char *s = strchr(*env, '=');
542 if (s++ != NULL) {
543 const long len = strlen(s);
544 if (RSTRING_LEN(value) == len
545 && strncmp(s, RSTRING_PTR(value), len) == 0) {
546 return env_str_new(*env, s - *env - 1);
547 }
548 }
549 env++;
550 }
551 return Qnil;
552 }
553
554 static VALUE
555 env_index(VALUE dmy, SEL sel, VALUE value)
556 {
557 rb_warn("ENV.index is deprecated; use ENV.key");
558 return env_key(dmy, 0, value);
559 }
560
561 static VALUE
562 env_to_hash(VALUE rcv, SEL sel)
563 {
564 rb_secure(4);
565
566 VALUE hash = rb_hash_new();
567 char **env = GET_ENVIRON();
568 while (*env != NULL) {
569 const char *s = strchr(*env, '=');
570 if (s != NULL) {
571 rb_hash_aset(hash, env_str_new(*env, s - *env),
572 env_str_new2(s + 1));
573 }
574 env++;
575 }
576 return hash;
577 }
578
579 static VALUE
580 env_reject(VALUE rcv, SEL sel)
581 {
582 rb_secure(4);
583
584 RETURN_ENUMERATOR(rcv, 0, 0);
585
586 VALUE hash = rb_hash_new();
587 char **env = GET_ENVIRON();
588 while (*env != NULL) {
589 const char *s = strchr(*env, '=');
590 if (s != NULL) {
591 VALUE key = env_str_new(*env, s - *env);
592 VALUE val = env_str_new2(s + 1);
593 if (!RTEST(rb_yield_values(2, key, val))) {
594 rb_hash_aset(hash, key, val);
595 }
596 }
597 env++;
598 }
599 return hash;
600 }
601
602 static VALUE
603 env_shift(VALUE rcv, SEL sel)
604 {
605 rb_secure(4);
606
607 char **env = GET_ENVIRON();
608 if (*env != NULL) {
609 const char *s = strchr(*env, '=');
610 if (s != NULL) {
611 VALUE key = env_str_new(*env, s - *env);
612 VALUE val = env_str_new2(getenv(RSTRING_PTR(key)));
613 env_delete(Qnil, key);
614 return rb_assoc_new(key, val);
615 }
616 }
617 return Qnil;
618 }
619
620 static VALUE
621 env_invert(VALUE rcv, SEL sel)
622 {
623 rb_secure(4);
624
625 VALUE hash = rb_hash_new();
626 char **env = GET_ENVIRON();
627 while (*env != NULL) {
628 const char *s = strchr(*env, '=');
629 if (s != NULL) {
630 rb_hash_aset(hash, env_str_new2(s + 1),
631 env_str_new(*env, s - *env));
632 }
633 env++;
634 }
635 return hash;
636 }
637
638 static int
639 env_replace_i(VALUE key, VALUE val, VALUE keys)
640 {
641 if (key != Qundef) {
642 env_aset(Qnil, 0, key, val);
643 if (rb_ary_includes(keys, key)) {
644 rb_ary_delete(keys, key);
645 }
646 }
647 return ST_CONTINUE;
648 }
649
650 static VALUE
651 env_replace(VALUE env, SEL sel, VALUE hash)
652 {
653 VALUE keys = env_keys(Qnil, 0); /* rb_secure(4); */
654 if (env == hash) {
655 return env;
656 }
657 hash = to_hash(hash);
658 rb_hash_foreach(hash, env_replace_i, keys);
659
660 for (long i = 0, count = RARRAY_LEN(keys); i < count; i++) {
661 env_delete(env, RARRAY_AT(keys, i));
662 }
663 return env;
664 }
665
666 static int
667 env_update_i(VALUE key, VALUE val)
668 {
669 if (key != Qundef) {
670 if (rb_block_given_p()) {
671 val = rb_yield_values(3, key, rb_f_getenv(Qnil, 0, key), val);
672 RETURN_IF_BROKEN();
673 }
674 env_aset(Qnil, 0, key, val);
675 }
676 return ST_CONTINUE;
677 }
678
679 static VALUE
680 env_update(VALUE env, SEL sel, VALUE hash)
681 {
682 rb_secure(4);
683 if (env == hash) {
684 return env;
685 }
686 hash = to_hash(hash);
687 rb_hash_foreach(hash, env_update_i, 0);
688 return env;
689 }
690
691 void
692 Init_ENV(void)
693 {
694 origenviron = GET_ENVIRON();
695 envtbl = rb_obj_alloc(rb_cObject);
696 rb_extend_object(envtbl, rb_mEnumerable);
697
698 VALUE klass = rb_singleton_class(envtbl);
699
700 rb_objc_define_method(klass, "[]", rb_f_getenv, 1);
701 rb_objc_define_method(klass, "fetch", env_fetch, -1);
702 rb_objc_define_method(klass, "[]=", env_aset, 2);
703 rb_objc_define_method(klass, "store", env_aset, 2);
704 rb_objc_define_method(klass, "each", env_each_pair, 0);
705 rb_objc_define_method(klass, "each_pair", env_each_pair, 0);
706 rb_objc_define_method(klass, "each_key", env_each_key, 0);
707 rb_objc_define_method(klass, "each_value", env_each_value, 0);
708 rb_objc_define_method(klass, "delete", env_delete_m, 1);
709 rb_objc_define_method(klass, "delete_if", env_delete_if, 0);
710 rb_objc_define_method(klass, "clear", rb_env_clear_imp, 0);
711 rb_objc_define_method(klass, "reject", env_reject, 0);
712 rb_objc_define_method(klass, "reject!", env_reject_bang, 0);
713 rb_objc_define_method(klass, "select", env_select, 0);
714 rb_objc_define_method(klass, "shift", env_shift, 0);
715 rb_objc_define_method(klass, "invert", env_invert, 0);
716 rb_objc_define_method(klass, "replace", env_replace, 1);
717 rb_objc_define_method(klass, "update", env_update, 1);
718 rb_objc_define_method(klass, "inspect", env_inspect, 0);
719 rb_objc_define_method(klass, "rehash", env_none, 0);
720 rb_objc_define_method(klass, "to_a", env_to_a, 0);
721 rb_objc_define_method(klass, "to_s", env_to_s, 0);
722 rb_objc_define_method(klass, "key", env_key, 1);
723 rb_objc_define_method(klass, "index", env_index, 1);
724 rb_objc_define_method(klass, "size", env_size, 0);
725 rb_objc_define_method(klass, "length", env_size, 0);
726 rb_objc_define_method(klass, "empty?", env_empty_p, 0);
727 rb_objc_define_method(klass, "keys", env_keys, 0);
728 rb_objc_define_method(klass, "values", env_values, 0);
729 rb_objc_define_method(klass, "values_at", env_values_at, -1);
730 rb_objc_define_method(klass, "include?", env_has_key, 1);
731 rb_objc_define_method(klass, "member?", env_has_key, 1);
732 rb_objc_define_method(klass, "has_key?", env_has_key, 1);
733 rb_objc_define_method(klass, "has_value?", env_has_value, 1);
734 rb_objc_define_method(klass, "key?", env_has_key, 1);
735 rb_objc_define_method(klass, "value?", env_has_value, 1);
736 rb_objc_define_method(klass, "to_hash", env_to_hash, 0);
737 rb_objc_define_method(klass, "assoc", env_assoc, 1);
738 rb_objc_define_method(klass, "rassoc", env_rassoc, 1);
739
740 rb_define_global_const("ENV", envtbl);
741 }
Something went wrong with that request. Please try again.