diff --git a/ChangeLog b/ChangeLog index 748eaf6995..548fad86d8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,42 @@ +Mon Jun 18 17:38:50 2001 Yukihiro Matsumoto + + * eval.c (rb_f_require): searches ".rb" and ".so" at the same + time. previous behavior (search ".rb", then ".so") has a + security risk (ruby-bugs#PR140). + + * array.c (rb_ary_to_ary): new function to replace internal + rb_Array(), which never calls to_a, but to_ary (rb_Array() might + call both). [new] + +Mon Jun 18 00:43:20 2001 Yukihiro Matsumoto + + * regex.c (PUSH_FAILURE_POINT): push option status again. + + * regex.c (re_compile_pattern): avoid pushing unnecessary + option_set. + +Sat Jun 16 10:58:48 2001 Yukihiro Matsumoto + + * eval.c (rb_load): tainted string is OK if wrapped *and* + $SAFE >= 4. + +Thu Jun 14 16:27:07 2001 Yukihiro Matsumoto + + * eval.c (rb_thread_start_0): should not nail down higher blocks + before preserving original context (i.e. should not alter + original context). + Wed Jun 13 19:34:59 2001 Akinori MUSHA * dir.c (Init_Dir): add a new method File::fnmatch? along with File::Constants::FNM_*. While I am here, FNM_NOCASE is renamed to FNM_CASEFOLD which is commonly used by *BSD and GNU libc. +Wed Jun 13 09:33:45 2001 Yukihiro Matsumoto + + * eval.c (proc_yield): new method equivalent to Proc#call but no + check for number of arguments. [new] + Tue Jun 12 14:21:28 2001 Nobuyoshi Nakada * lib/mkmf.rb: target_prefix is only for installation, not for diff --git a/ToDo b/ToDo index 7a22319bef..e9d0d74adf 100644 --- a/ToDo +++ b/ToDo @@ -83,6 +83,7 @@ Standard Libraries * hash etc. should handle self referenceing array/hash * move NameError under StandardError. * library to load per-user profile seeking .ruby_profile or ruby.ini file. +* warning framework Extension Libraries diff --git a/array.c b/array.c index 010afd9a39..d0f6977a13 100644 --- a/array.c +++ b/array.c @@ -17,6 +17,7 @@ #include "st.h" VALUE rb_cArray; +static ID cmp; #define ARY_DEFAULT_SIZE 16 @@ -729,6 +730,20 @@ to_ary(ary) return rb_convert_type(ary, T_ARRAY, "Array", "to_ary"); } +VALUE +rb_ary_to_ary(obj) + VALUE obj; +{ + if (NIL_P(obj)) return rb_ary_new2(0); + if (TYPE(obj) == T_ARRAY) { + return obj; + } + if (rb_respond_to(obj, rb_intern("to_ary"))) { + return rb_convert_type(obj, T_ARRAY, "Array", "to_ary"); + } + return rb_ary_new3(1, obj); +} + extern VALUE rb_output_fs; static VALUE @@ -958,8 +973,6 @@ rb_ary_reverse_m(ary) return rb_ary_reverse(rb_obj_dup(ary)); } -static ID cmp; - static int sort_1(a, b) VALUE *a, *b; diff --git a/eval.c b/eval.c index 80905ba8af..314744b0ac 100644 --- a/eval.c +++ b/eval.c @@ -1676,7 +1676,7 @@ copy_node_scope(node, rval) char *file = ruby_sourcefile;\ int line = ruby_sourceline;\ if (TYPE(args) != T_ARRAY)\ - args = rb_Array(args);\ + args = rb_ary_to_ary(args);\ argc = RARRAY(args)->len;\ argv = ALLOCA_N(VALUE, argc);\ MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);\ @@ -2129,7 +2129,7 @@ rb_eval(self, n) VALUE v = rb_eval(self, tag->nd_head->nd_head); int i; - if (TYPE(v) != T_ARRAY) v = rb_Array(v); + if (TYPE(v) != T_ARRAY) v = rb_ary_to_ary(v); for (i=0; ilen; i++) { if (RTEST(RARRAY(v)->ptr[i])) { node = node->nd_body; @@ -2174,7 +2174,7 @@ rb_eval(self, n) VALUE v = rb_eval(self, tag->nd_head->nd_head); int i; - if (TYPE(v) != T_ARRAY) v = rb_Array(v); + if (TYPE(v) != T_ARRAY) v = rb_ary_to_ary(v); for (i=0; ilen; i++) { if (RTEST(rb_funcall2(RARRAY(v)->ptr[i], eqq, 1, &val))){ node = node->nd_body; @@ -2347,14 +2347,14 @@ rb_eval(self, n) case NODE_RESTARY: result = rb_eval(self, node->nd_head); if (TYPE(result) != T_ARRAY) { - result = rb_Array(result); + result = rb_ary_to_ary(result); } break; case NODE_REXPAND: result = rb_eval(self, node->nd_head); if (TYPE(result) != T_ARRAY) { - result = rb_Array(result); + result = rb_ary_to_ary(result); } if (RARRAY(result)->len == 0) { result = Qnil; @@ -2520,7 +2520,7 @@ rb_eval(self, n) case NODE_ARGSCAT: result = rb_ary_concat(rb_eval(self, node->nd_head), - rb_Array(rb_eval(self, node->nd_body))); + rb_ary_to_ary(rb_eval(self, node->nd_body))); break; case NODE_ARGSPUSH: @@ -3546,16 +3546,20 @@ rb_f_block_given_p() return Qfalse; } +#define PC_NONE 0x0 +#define PC_ACHECK 0x1 +#define PC_PCALL 0x2 + static VALUE -rb_yield_0(val, self, klass, acheck) +rb_yield_0(val, self, klass, pcall) VALUE val, self, klass; /* OK */ - int acheck; + int pcall; { NODE *node; volatile VALUE result = Qnil; volatile VALUE old_cref; - struct BLOCK *block; - struct SCOPE *old_scope; + struct BLOCK * volatile block; + struct SCOPE * volatile old_scope; struct FRAME frame; int state; static unsigned serial = 1; @@ -3591,7 +3595,7 @@ rb_yield_0(val, self, klass, acheck) PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { if (block->var == (NODE*)1) { - if (acheck && val != Qundef && + if ((pcall&PC_ACHECK) && val != Qundef && TYPE(val) == T_ARRAY && RARRAY(val)->len != 0) { rb_raise(rb_eArgError, "wrong # of arguments (%d for 0)", RARRAY(val)->len); @@ -3605,14 +3609,14 @@ rb_yield_0(val, self, klass, acheck) } else { if (nd_type(block->var) == NODE_MASGN) - massign(self, block->var, val, acheck); + massign(self, block->var, val, (pcall&PC_ACHECK)); else { /* argument adjust for proc_call etc. */ - if (acheck && val != Qundef && + if (pcall && val != Qundef && TYPE(val) == T_ARRAY && RARRAY(val)->len == 1) { val = RARRAY(val)->ptr[0]; } - assign(self, block->var, val, acheck); + assign(self, block->var, val, (pcall&PC_ACHECK)); } } } @@ -3621,7 +3625,7 @@ rb_yield_0(val, self, klass, acheck) } else { /* argument adjust for proc_call etc. */ - if (acheck && val != Qundef && + if (pcall && val != Qundef && TYPE(val) == T_ARRAY && RARRAY(val)->len == 1) { val = RARRAY(val)->ptr[0]; } @@ -5171,7 +5175,12 @@ rb_load(fname, wrap) NODE *saved_cref = ruby_cref; TMP_PROTECT; - SafeStringValue(fname); + if (wrap && ruby_safe_level >= 4) { + StringValue(fname); + } + else { + SafeStringValue(fname); + } file = rb_find_file(RSTRING(fname)->ptr); if (!file) { rb_raise(rb_eLoadError, "No such file to load -- %s", RSTRING(fname)->ptr); @@ -5402,28 +5411,20 @@ rb_f_require(obj, fname) } buf = ALLOCA_N(char, strlen(RSTRING(fname)->ptr) + 5); strcpy(buf, RSTRING(fname)->ptr); - strcat(buf, ".rb"); - if (rb_find_file(buf)) { + switch (rb_find_file_noext(buf)) { + case 0: + break; + + case 1: fname = rb_str_new2(buf); - feature = buf; + file = feature = buf; goto load_rb; - } - strcpy(buf, RSTRING(fname)->ptr); - strcat(buf, DLEXT); - file = rb_find_file(buf); - if (file) { + + default: feature = buf; + file = rb_find_file(buf); goto load_dyna; } -#ifdef DLEXT2 - strcpy(buf, RSTRING(fname)->ptr); - strcat(buf, DLEXT2); - file = rb_find_file(buf); - if (file) { - feature = buf; - goto load_dyna; - } -#endif rb_raise(rb_eLoadError, "No such file to load -- %s", RSTRING(fname)->ptr); @@ -6330,8 +6331,9 @@ callargs(args) } static VALUE -proc_call(proc, args) +proc_invoke(proc, args, pcall) VALUE proc, args; /* OK */ + int pcall; { struct BLOCK * volatile old_block; struct BLOCK _block; @@ -6341,6 +6343,13 @@ proc_call(proc, args) volatile int orphan; volatile int safe = ruby_safe_level; + if (pcall) { + pcall = PC_ACHECK|PC_PCALL; + } + else { + pcall = PC_PCALL; + } + if (rb_block_given_p() && ruby_frame->last_func) { rb_warning("block for %s#%s is useless", rb_class2name(CLASS_OF(proc)), @@ -6367,7 +6376,7 @@ proc_call(proc, args) state = EXEC_TAG(); if (state == 0) { proc_set_safe_level(proc); - result = rb_yield_0(args, 0, 0, Qtrue); + result = rb_yield_0(args, 0, 0, pcall); } POP_TAG(); @@ -6398,6 +6407,20 @@ proc_call(proc, args) return result; } +static VALUE +proc_call(proc, args) + VALUE proc, args; /* OK */ +{ + return proc_invoke(proc, args, Qtrue); +} + +static VALUE +proc_yield(proc, args) + VALUE proc, args; /* OK */ +{ + return proc_invoke(proc, args, Qfalse); +} + static VALUE proc_arity(proc) VALUE proc; @@ -6899,6 +6922,7 @@ Init_Proc() rb_define_singleton_method(rb_cProc, "new", proc_s_new, -1); rb_define_method(rb_cProc, "call", proc_call, -2); + rb_define_method(rb_cProc, "yield", proc_yield, -2); rb_define_method(rb_cProc, "arity", proc_arity, 0); rb_define_method(rb_cProc, "[]", proc_call, -2); rb_define_method(rb_cProc, "==", proc_eq, 1); @@ -8210,7 +8234,11 @@ rb_thread_start_0(fn, arg, th_arg) } #endif - if (ruby_block) { /* should nail down higher scopes */ + if (THREAD_SAVE_CONTEXT(curr_thread)) { + return thread; + } + + if (ruby_block) { /* should nail down higher blocks */ struct BLOCK dummy; dummy.prev = ruby_block; @@ -8219,9 +8247,6 @@ rb_thread_start_0(fn, arg, th_arg) } scope_dup(ruby_scope); FL_SET(ruby_scope, SCOPE_SHARED); - if (THREAD_SAVE_CONTEXT(curr_thread)) { - return thread; - } if (!th->next) { /* merge in thread list */ @@ -8243,17 +8268,21 @@ rb_thread_start_0(fn, arg, th_arg) POP_TAG(); status = th->status; + if (th == main_thread) ruby_stop(state); + rb_thread_remove(th); + while (saved_block) { struct BLOCK *tmp = saved_block; + if (curr_thread == main_thread) { + printf("free(%p)\n", saved_block); + } if (tmp->frame.argc > 0) free(tmp->frame.argv); saved_block = tmp->prev; free(tmp); } - if (th == main_thread) ruby_stop(state); - rb_thread_remove(th); if (state && status != THREAD_TO_KILL && !NIL_P(ruby_errinfo)) { th->flags |= THREAD_RAISED; if (state == TAG_FATAL) { diff --git a/file.c b/file.c index 1cff4778ca..66f82bcc58 100644 --- a/file.c +++ b/file.c @@ -2202,41 +2202,76 @@ is_macos_native_path(path) } #endif +static char* +file_load_ok(file) + char *file; +{ + FILE *f; + + f = fopen(file, "r"); + if (f == NULL) return 0; + fclose(f); + return file; +} + +extern VALUE rb_load_path; + +int +rb_find_file_noext(file) + char *file; +{ + char *path, *e, *found; + char *fend = file + strlen(file); + VALUE fname; + int i, j; + + static char *ext[] = { + ".rb", DLEXT, +#ifdef DLEXT2 + DLEXT2, +#endif + 0 + }; + + if (file[0] == '~') { + fname = rb_str_new2(file); + fname = rb_file_s_expand_path(1, &fname); + file = StringValuePtr(fname); + } + + if (is_absolute_path(file)) { + for (i=0; ext[i]; i++) { + strcpy(fend, ext[i]); + if (file_load_ok(file)) return i+1; + } + return 0; + } + + if (!rb_load_path) return 0; + + Check_Type(rb_load_path, T_ARRAY); + for (i=0;ilen;i++) { + VALUE str = RARRAY(rb_load_path)->ptr[i]; + + SafeStringValue(str); + path = RSTRING(str)->ptr; + for (j=0; ext[j]; j++) { + strcpy(fend, ext[j]); + found = dln_find_file(file, path); + if (found && file_load_ok(found)) return j+1; + } + } + return 0; +} + char* rb_find_file(file) char *file; { - extern VALUE rb_load_path; VALUE vpath, fname; char *path; struct stat st; -#if defined(__MACOS__) || defined(riscos) - if (is_macos_native_path(file)) { - FILE *f; - - if (rb_safe_level() >= 2 && !rb_path_check(file)) { - rb_raise(rb_eSecurityError, "loading from unsafe file %s", file); - } - f= fopen(file, "r"); - if (f == NULL) return 0; - fclose(f); - return file; - } -#endif - - if (is_absolute_path(file)) { - FILE *f; - - if (rb_safe_level() >= 2 && !rb_path_check(file)) { - rb_raise(rb_eSecurityError, "loading from unsafe file %s", file); - } - f = fopen(file, "r"); - if (f == NULL) return 0; - fclose(f); - return file; - } - if (file[0] == '~') { fname = rb_str_new2(file); fname = rb_file_s_expand_path(1, &fname); @@ -2246,6 +2281,22 @@ rb_find_file(file) file = StringValuePtr(fname); } +#if defined(__MACOS__) || defined(riscos) + if (is_macos_native_path(file)) { + if (rb_safe_level() >= 2 && !rb_path_check(file)) { + rb_raise(rb_eSecurityError, "loading from unsafe file %s", file); + } + return file_load_ok(file); + } +#endif + + if (is_absolute_path(file)) { + if (rb_safe_level() >= 2 && !rb_path_check(file)) { + rb_raise(rb_eSecurityError, "loading from unsafe file %s", file); + } + return file_load_ok(file); + } + if (rb_load_path) { int i; @@ -2269,10 +2320,7 @@ rb_find_file(file) } path = dln_find_file(file, path); - if (path && stat(path, &st) == 0) { - return path; - } - return 0; + return file_load_ok(path); } static void diff --git a/intern.h b/intern.h index 866df5845d..ebfc57cffc 100644 --- a/intern.h +++ b/intern.h @@ -27,6 +27,7 @@ VALUE rb_ary_new4 _((long, VALUE *)); VALUE rb_ary_freeze _((VALUE)); VALUE rb_ary_aref _((int, VALUE*, VALUE)); void rb_ary_store _((VALUE, long, VALUE)); +VALUE rb_ary_to_ary _((VALUE)); VALUE rb_ary_to_s _((VALUE)); VALUE rb_ary_push _((VALUE, VALUE)); VALUE rb_ary_pop _((VALUE)); @@ -179,6 +180,7 @@ void rb_thread_atfork _((void)); int eaccess _((const char*, int)); VALUE rb_file_s_expand_path _((int, VALUE *)); void rb_file_const _((const char*, VALUE)); +int rb_find_file_noext _((char*)); char *rb_find_file _((char*)); /* gc.c */ void rb_gc_mark_locations _((VALUE*, VALUE*)); diff --git a/io.c b/io.c index 3bcf663aad..ae021f8a4b 100644 --- a/io.c +++ b/io.c @@ -3462,10 +3462,8 @@ opt_i_set(val) VALUE val; { if (ruby_inplace_mode) free(ruby_inplace_mode); - if (!RTEST(val)) { - ruby_inplace_mode = 0; - return; - } + ruby_inplace_mode = 0; + if (!RTEST(val)) return; StringValue(val); ruby_inplace_mode = strdup(RSTRING(val)->ptr); } diff --git a/lib/resolv.rb b/lib/resolv.rb index 782501b7cc..16cf4b5c88 100644 --- a/lib/resolv.rb +++ b/lib/resolv.rb @@ -854,7 +854,7 @@ class Resolv raise DecodeError.new("limit exceed") if @limit < @index + len arr = @data.unpack("@#{@index}#{template}") @index += len - return *arr + return arr end def get_string diff --git a/misc/ruby-mode.el b/misc/ruby-mode.el index 102de81b5a..da31f989d1 100644 --- a/misc/ruby-mode.el +++ b/misc/ruby-mode.el @@ -660,21 +660,24 @@ An end of a defun is found by moving forward from the beginning of one." (cond ((featurep 'font-lock) + (or (boundp 'font-lock-variable-name-face) + (setq font-lock-variable-name-face font-lock-type-face)) - (setq ruby-font-lock-syntactic-keywords - '(("\\$\\([#\"'`$\\]\\)" 1 (1 . nil)) - ("\\(#\\)[{$@]" 1 (1 . nil)) - ("\\(/\\)\\([^/\n]\\|\\/\\)*\\(/\\)" - (1 (7 . ?')) - (3 (7 . ?'))) - ("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil)) - ("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil)))) - (put major-mode 'font-lock-defaults - '((ruby-font-lock-keywords) - nil nil nil - beginning-of-line - (font-lock-syntactic-keywords - . ruby-font-lock-syntactic-keywords))) + + (add-hook 'ruby-mode-hook + '(lambda () + (make-local-variable 'font-lock-syntactic-keywords) + (setq font-lock-syntactic-keywords + '(("\\$\\([#\"'`$\\]\\)" 1 (1 . nil)) + ("\\(#\\)[{$@]" 1 (1 . nil)) + ("\\(/\\)\\([^/\n]\\|\\/\\)*\\(/\\)" + (1 (7 . ?')) + (3 (7 . ?'))) + ("^\\(=\\)begin\\(\\s \\|$\\)" 1 (7 . nil)) + ("^\\(=\\)end\\(\\s \\|$\\)" 1 (7 . nil)))) + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '((ruby-font-lock-keywords) nil nil)) + (setq font-lock-keywords ruby-font-lock-keywords))) (defun ruby-font-lock-docs (limit) (if (re-search-forward "^=begin\\(\\s \\|$\\)" limit t) @@ -687,6 +690,21 @@ An end of a defun is found by moving forward from the beginning of one." (set-match-data (list beg (point))) t))))) + (defun ruby-font-lock-maybe-docs (limit) + (let (beg) + (save-excursion + (if (and (re-search-backward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t) + (string= (match-string-no-properties 1) "begin")) + (progn + (beginning-of-line) + (setq beg (point))))) + (if (and beg (and (re-search-forward "^=\\(begin\\|end\\)\\(\\s \\|$\\)" nil t) + (string= (match-string-no-properties 1) "end"))) + (progn + (set-match-data (list beg (point))) + t) + nil))) + (defvar ruby-font-lock-keywords (list (cons (concat @@ -741,6 +759,8 @@ An end of a defun is found by moving forward from the beginning of one." ;; embedded document '(ruby-font-lock-docs 0 font-lock-comment-face t) + '(ruby-font-lock-maybe-docs + 0 font-lock-comment-face t) ;; constants '("\\(^\\|[^_]\\)\\b\\([A-Z]+\\(\\w\\|_\\)*\\)" 2 font-lock-type-face) diff --git a/regex.c b/regex.c index 39e65da9b9..e40c0e4ff2 100644 --- a/regex.c +++ b/regex.c @@ -1874,8 +1874,10 @@ re_compile_pattern(pattern, size, bufp) if ((options ^ stackp[-1]) & RE_OPTION_IGNORECASE) { BUFPUSH((options&RE_OPTION_IGNORECASE)?casefold_off:casefold_on); } - BUFPUSH(option_set); - BUFPUSH(stackp[-1]); + if ((options ^ stackp[-1]) != RE_OPTION_IGNORECASE) { + BUFPUSH(option_set); + BUFPUSH(stackp[-1]); + } } p0 = b; options = *--stackp; @@ -3262,7 +3264,8 @@ re_search(bufp, string, size, startpos, range, regs) } if (startpos > size) return -1; - if (anchor && size > 0 && startpos == size) return -1; + if ((anchor || !bufp->can_be_null) && size > 0 && startpos == size) + return -1; val = re_match(bufp, string, size, startpos, regs); if (val >= 0) return startpos; if (val == -2) return -2; @@ -3362,7 +3365,7 @@ re_search(bufp, string, size, startpos, range, regs) #define NUM_COUNT_ITEMS 2 /* Individual items aside from the registers. */ -#define NUM_NONREG_ITEMS 3 +#define NUM_NONREG_ITEMS 4 /* We push at most this many things on the stack whenever we fail. The `+ 2' refers to PATTERN_PLACE and STRING_PLACE, which are @@ -3412,6 +3415,7 @@ re_search(bufp, string, size, startpos, range, regs) \ *stackp++ = pattern_place; \ *stackp++ = string_place; \ + *stackp++ = (unsigned char*)options; /* current option status */ \ *stackp++ = (unsigned char*)0; /* non-greedy flag */ \ } while(0) @@ -3735,15 +3739,11 @@ re_match(bufp, string_arg, size, pos, regs) int regno = *p++; /* Get which register to match against */ register unsigned char *d2, *dend2; -#if 0 /* Check if corresponding group is still open */ if (IS_ACTIVE(reg_info[regno])) goto fail; /* Where in input to try to start matching. */ d2 = regstart[regno]; -#else - d2 = IS_ACTIVE(reg_info[regno])?old_regstart[regno]:regstart[regno]; -#endif if (REG_UNSET(d2)) goto fail; /* Where to stop matching; if both the place to start and @@ -3791,7 +3791,7 @@ re_match(bufp, string_arg, size, pos, regs) case stop_nowidth: EXTRACT_NUMBER_AND_INCR(mcnt, p); stackp = stackb + mcnt; - d = stackp[-2]; + d = stackp[-3]; POP_FAILURE_POINT(); continue; @@ -4015,8 +4015,8 @@ re_match(bufp, string_arg, size, pos, regs) because didn't fail. Also remove the register information put on by the on_failure_jump. */ case finalize_jump: - if (stackp > stackb && stackp[-2] == d) { - p = stackp[-3]; + if (stackp > stackb && stackp[-3] == d) { + p = stackp[-4]; POP_FAILURE_POINT(); continue; } @@ -4032,7 +4032,7 @@ re_match(bufp, string_arg, size, pos, regs) case jump: nofinalize: EXTRACT_NUMBER_AND_INCR(mcnt, p); - if (mcnt < 0 && stackp > stackb && stackp[-2] == d) /* avoid infinite loop */ + if (mcnt < 0 && stackp > stackb && stackp[-3] == d) /* avoid infinite loop */ goto fail; p += mcnt; continue; @@ -4123,7 +4123,7 @@ re_match(bufp, string_arg, size, pos, regs) case finalize_push: POP_FAILURE_POINT(); EXTRACT_NUMBER_AND_INCR(mcnt, p); - if (mcnt < 0 && stackp > stackb && stackp[-2] == d) /* avoid infinite loop */ + if (mcnt < 0 && stackp > stackb && stackp[-3] == d) /* avoid infinite loop */ goto fail; PUSH_FAILURE_POINT(p + mcnt, d); stackp[-1] = NON_GREEDY; @@ -4288,11 +4288,12 @@ re_match(bufp, string_arg, size, pos, regs) /* If this failure point is from a dummy_failure_point, just skip it. */ - if (stackp[-3] == 0 || (best_regs_set && stackp[-1] == NON_GREEDY)) { + if (stackp[-4] == 0 || (best_regs_set && stackp[-1] == NON_GREEDY)) { POP_FAILURE_POINT(); goto fail; } - stackp--; /* discard flag */ + stackp--; /* discard greedy flag */ + options = (int)*--stackp; d = *--stackp; p = *--stackp; /* Restore register info. */