2 * Copyright 2008 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
34 jsval_t last_index_val;
37 static const WCHAR sourceW[] = {'s','o','u','r','c','e',0};
38 static const WCHAR globalW[] = {'g','l','o','b','a','l',0};
39 static const WCHAR ignoreCaseW[] = {'i','g','n','o','r','e','C','a','s','e',0};
40 static const WCHAR multilineW[] = {'m','u','l','t','i','l','i','n','e',0};
41 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0};
42 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
43 static const WCHAR execW[] = {'e','x','e','c',0};
44 static const WCHAR testW[] = {'t','e','s','t',0};
46 static const WCHAR leftContextW[] =
47 {'l','e','f','t','C','o','n','t','e','x','t',0};
48 static const WCHAR rightContextW[] =
49 {'r','i','g','h','t','C','o','n','t','e','x','t',0};
51 static const WCHAR idx1W[] = {'$','1',0};
52 static const WCHAR idx2W[] = {'$','2',0};
53 static const WCHAR idx3W[] = {'$','3',0};
54 static const WCHAR idx4W[] = {'$','4',0};
55 static const WCHAR idx5W[] = {'$','5',0};
56 static const WCHAR idx6W[] = {'$','6',0};
57 static const WCHAR idx7W[] = {'$','7',0};
58 static const WCHAR idx8W[] = {'$','8',0};
59 static const WCHAR idx9W[] = {'$','9',0};
61 static const WCHAR emptyW[] = {0};
63 static inline RegExpInstance *regexp_from_vdisp(vdisp_t *vdisp)
65 return (RegExpInstance*)vdisp->u.jsdisp;
68 static void set_last_index(RegExpInstance *This, DWORD last_index)
70 This->last_index = last_index;
71 jsval_release(This->last_index_val);
72 This->last_index_val = jsval_number(last_index);
75 static HRESULT do_regexp_match_next(script_ctx_t *ctx, RegExpInstance *regexp,
76 DWORD rem_flags, jsstr_t *str, match_state_t *ret)
80 hres = regexp_execute(regexp->jsregexp, ctx, &ctx->tmp_heap,
81 str->str, jsstr_length(str), ret);
85 if(rem_flags & REM_RESET_INDEX)
86 set_last_index(regexp, 0);
90 if(!(rem_flags & REM_NO_CTX_UPDATE) && ctx->last_match != str) {
91 jsstr_release(ctx->last_match);
92 ctx->last_match = jsstr_addref(str);
95 if(!(rem_flags & REM_NO_CTX_UPDATE)) {
96 DWORD i, n = min(sizeof(ctx->match_parens)/sizeof(ctx->match_parens[0]), ret->paren_count);
98 for(i=0; i < n; i++) {
99 if(ret->parens[i].index == -1) {
100 ctx->match_parens[i].index = 0;
101 ctx->match_parens[i].length = 0;
103 ctx->match_parens[i].index = ret->parens[i].index;
104 ctx->match_parens[i].length = ret->parens[i].length;
108 if(n < sizeof(ctx->match_parens)/sizeof(ctx->match_parens[0]))
109 memset(ctx->match_parens+n, 0, sizeof(ctx->match_parens) - n*sizeof(ctx->match_parens[0]));
112 set_last_index(regexp, ret->cp-str->str);
114 if(!(rem_flags & REM_NO_CTX_UPDATE)) {
115 ctx->last_match_index = ret->cp-str->str-ret->match_len;
116 ctx->last_match_length = ret->match_len;
122 HRESULT regexp_match_next(script_ctx_t *ctx, jsdisp_t *dispex,
123 DWORD rem_flags, jsstr_t *str, match_state_t **ret)
125 RegExpInstance *regexp = (RegExpInstance*)dispex;
126 match_state_t *match;
130 if((rem_flags & REM_CHECK_GLOBAL) && !(regexp->jsregexp->flags & REG_GLOB)) {
131 if(rem_flags & REM_ALLOC_RESULT)
136 if(rem_flags & REM_ALLOC_RESULT) {
137 match = alloc_match_state(regexp->jsregexp, NULL, str->str);
139 return E_OUTOFMEMORY;
143 mark = heap_pool_mark(&ctx->tmp_heap);
145 if(rem_flags & REM_NO_PARENS) {
146 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, NULL);
148 heap_pool_clear(mark);
149 return E_OUTOFMEMORY;
151 match->cp = (*ret)->cp;
152 match->match_len = (*ret)->match_len;
157 hres = do_regexp_match_next(ctx, regexp, rem_flags, str, match);
159 if(rem_flags & REM_NO_PARENS) {
160 (*ret)->cp = match->cp;
161 (*ret)->match_len = match->match_len;
164 heap_pool_clear(mark);
166 if(hres != S_OK && (rem_flags & REM_ALLOC_RESULT)) {
174 static HRESULT regexp_match(script_ctx_t *ctx, jsdisp_t *dispex, jsstr_t *str, BOOL gflag,
175 match_result_t **match_result, DWORD *result_cnt)
177 RegExpInstance *This = (RegExpInstance*)dispex;
178 match_result_t *ret = NULL;
179 match_state_t *result;
180 DWORD i=0, ret_size = 0;
184 mark = heap_pool_mark(&ctx->tmp_heap);
186 result = alloc_match_state(This->jsregexp, &ctx->tmp_heap, str->str);
188 heap_pool_clear(mark);
189 return E_OUTOFMEMORY;
193 hres = do_regexp_match_next(ctx, This, 0, str, result);
194 if(hres == S_FALSE) {
204 match_result_t *old_ret = ret;
206 ret = heap_realloc(old_ret, (ret_size <<= 1) * sizeof(match_result_t));
210 ret = heap_alloc((ret_size=4) * sizeof(match_result_t));
213 hres = E_OUTOFMEMORY;
218 ret[i].index = result->cp - str->str - result->match_len;
219 ret[i++].length = result->match_len;
221 if(!gflag && !(This->jsregexp->flags & REG_GLOB)) {
227 heap_pool_clear(mark);
238 static HRESULT RegExp_source(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
244 case DISPATCH_PROPERTYGET: {
245 RegExpInstance *This = regexp_from_vdisp(jsthis);
246 *r = jsval_string(jsstr_addref(This->str));
250 FIXME("Unimplemented flags %x\n", flags);
257 static HRESULT RegExp_global(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
264 static HRESULT RegExp_ignoreCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
271 static HRESULT RegExp_multiline(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
278 static INT index_from_val(script_ctx_t *ctx, jsval_t v)
283 hres = to_number(ctx, v, &n);
285 clear_ei(ctx); /* FIXME: Move ignoring exceptions to to_primitive */
290 return is_int32(n) ? n : 0;
293 static HRESULT RegExp_lastIndex(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
299 case DISPATCH_PROPERTYGET: {
300 RegExpInstance *regexp = regexp_from_vdisp(jsthis);
302 return jsval_copy(regexp->last_index_val, r);
304 case DISPATCH_PROPERTYPUT: {
305 RegExpInstance *regexp = regexp_from_vdisp(jsthis);
308 hres = jsval_copy(argv[0], ®exp->last_index_val);
312 regexp->last_index = index_from_val(ctx, argv[0]);
316 FIXME("unimplemented flags: %x\n", flags);
323 static HRESULT RegExp_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
330 static HRESULT create_match_array(script_ctx_t *ctx, jsstr_t *input,
331 const match_state_t *result, IDispatch **ret)
338 static const WCHAR indexW[] = {'i','n','d','e','x',0};
339 static const WCHAR inputW[] = {'i','n','p','u','t',0};
340 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0};
341 static const WCHAR zeroW[] = {'0',0};
343 hres = create_array(ctx, result->paren_count+1, &array);
347 for(i=0; i < result->paren_count; i++) {
348 if(result->parens[i].index != -1)
349 str = jsstr_substr(input, result->parens[i].index, result->parens[i].length);
353 hres = E_OUTOFMEMORY;
357 hres = jsdisp_propput_idx(array, i+1, jsval_string(str));
363 while(SUCCEEDED(hres)) {
364 hres = jsdisp_propput_name(array, indexW, jsval_number(result->cp-input->str-result->match_len));
368 hres = jsdisp_propput_name(array, lastIndexW, jsval_number(result->cp-input->str));
372 hres = jsdisp_propput_name(array, inputW, jsval_string(jsstr_addref(input)));
376 str = jsstr_alloc_len(result->cp-result->match_len, result->match_len);
378 hres = E_OUTOFMEMORY;
381 hres = jsdisp_propput_name(array, zeroW, jsval_string(str));
387 jsdisp_release(array);
391 *ret = to_disp(array);
395 static HRESULT run_exec(script_ctx_t *ctx, vdisp_t *jsthis, jsval_t arg,
396 jsstr_t **input, match_state_t **result, BOOL *ret)
398 RegExpInstance *regexp;
399 match_state_t *match;
400 DWORD last_index = 0;
404 if(!is_vclass(jsthis, JSCLASS_REGEXP)) {
405 FIXME("Not a RegExp\n");
409 regexp = regexp_from_vdisp(jsthis);
411 hres = to_string(ctx, arg, &string);
415 if(regexp->jsregexp->flags & REG_GLOB) {
416 if(regexp->last_index < 0) {
417 jsstr_release(string);
418 set_last_index(regexp, 0);
421 *input = jsstr_empty();
425 last_index = regexp->last_index;
428 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, string->str+last_index);
430 jsstr_release(string);
431 return E_OUTOFMEMORY;
434 hres = regexp_match_next(ctx, ®exp->dispex, REM_RESET_INDEX, string, &match);
436 jsstr_release(string);
445 jsstr_release(string);
449 static HRESULT RegExp_exec(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
452 match_state_t *match;
460 mark = heap_pool_mark(&ctx->tmp_heap);
462 hres = run_exec(ctx, jsthis, argc ? argv[0] : jsval_string(jsstr_empty()), &string, &match, &b);
464 heap_pool_clear(mark);
472 hres = create_match_array(ctx, string, match, &ret);
474 *r = jsval_disp(ret);
480 heap_pool_clear(mark);
481 jsstr_release(string);
485 static HRESULT RegExp_test(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
488 match_state_t *match;
496 mark = heap_pool_mark(&ctx->tmp_heap);
497 hres = run_exec(ctx, jsthis, argc ? argv[0] : jsval_string(undef_str = jsstr_undefined()), NULL, &match, &b);
498 heap_pool_clear(mark);
500 jsstr_release(undef_str);
509 static HRESULT RegExp_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
516 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
518 FIXME("unimplemented flags %x\n", flags);
525 static void RegExp_destructor(jsdisp_t *dispex)
527 RegExpInstance *This = (RegExpInstance*)dispex;
530 regexp_destroy(This->jsregexp);
531 jsval_release(This->last_index_val);
532 jsstr_release(This->str);
536 static const builtin_prop_t RegExp_props[] = {
537 {execW, RegExp_exec, PROPF_METHOD|1},
538 {globalW, RegExp_global, 0},
539 {ignoreCaseW, RegExp_ignoreCase, 0},
540 {lastIndexW, RegExp_lastIndex, 0},
541 {multilineW, RegExp_multiline, 0},
542 {sourceW, RegExp_source, 0},
543 {testW, RegExp_test, PROPF_METHOD|1},
544 {toStringW, RegExp_toString, PROPF_METHOD}
547 static const builtin_info_t RegExp_info = {
549 {NULL, RegExp_value, 0},
550 sizeof(RegExp_props)/sizeof(*RegExp_props),
556 static const builtin_prop_t RegExpInst_props[] = {
557 {globalW, RegExp_global, 0},
558 {ignoreCaseW, RegExp_ignoreCase, 0},
559 {lastIndexW, RegExp_lastIndex, 0},
560 {multilineW, RegExp_multiline, 0},
561 {sourceW, RegExp_source, 0}
564 static const builtin_info_t RegExpInst_info = {
566 {NULL, RegExp_value, 0},
567 sizeof(RegExpInst_props)/sizeof(*RegExpInst_props),
573 static HRESULT alloc_regexp(script_ctx_t *ctx, jsdisp_t *object_prototype, RegExpInstance **ret)
575 RegExpInstance *regexp;
578 regexp = heap_alloc_zero(sizeof(RegExpInstance));
580 return E_OUTOFMEMORY;
583 hres = init_dispex(®exp->dispex, ctx, &RegExp_info, object_prototype);
585 hres = init_dispex_from_constr(®exp->dispex, ctx, &RegExpInst_info, ctx->regexp_constr);
596 HRESULT create_regexp(script_ctx_t *ctx, jsstr_t *src, DWORD flags, jsdisp_t **ret)
598 RegExpInstance *regexp;
601 TRACE("%s %x\n", debugstr_jsstr(src), flags);
603 hres = alloc_regexp(ctx, NULL, ®exp);
607 regexp->str = jsstr_addref(src);
608 regexp->last_index_val = jsval_number(0);
610 regexp->jsregexp = regexp_new(ctx, &ctx->tmp_heap, regexp->str->str,
611 jsstr_length(regexp->str), flags, FALSE);
612 if(!regexp->jsregexp) {
613 WARN("regexp_new failed\n");
614 jsdisp_release(®exp->dispex);
618 *ret = ®exp->dispex;
622 HRESULT create_regexp_var(script_ctx_t *ctx, jsval_t src_arg, jsval_t *flags_arg, jsdisp_t **ret)
624 jsstr_t *src, *opt = NULL;
628 if(is_object_instance(src_arg)) {
631 obj = iface_to_jsdisp((IUnknown*)get_object(src_arg));
633 if(is_class(obj, JSCLASS_REGEXP)) {
634 RegExpInstance *regexp = (RegExpInstance*)obj;
636 hres = create_regexp(ctx, regexp->str, regexp->jsregexp->flags, ret);
645 if(!is_string(src_arg)) {
646 FIXME("src_arg = %s\n", debugstr_jsval(src_arg));
650 src = get_string(src_arg);
653 if(!is_string(*flags_arg)) {
654 FIXME("unimplemented for %s\n", debugstr_jsval(*flags_arg));
658 opt = get_string(*flags_arg);
661 hres = parse_regexp_flags(opt ? opt->str : NULL, opt ? jsstr_length(opt) : 0, &flags);
665 return create_regexp(ctx, src, flags, ret);
668 HRESULT regexp_string_match(script_ctx_t *ctx, jsdisp_t *re, jsstr_t *str, jsval_t *r)
670 static const WCHAR indexW[] = {'i','n','d','e','x',0};
671 static const WCHAR inputW[] = {'i','n','p','u','t',0};
672 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0};
674 RegExpInstance *regexp = (RegExpInstance*)re;
675 match_result_t *match_result;
676 unsigned match_cnt, i;
680 if(!(regexp->jsregexp->flags & REG_GLOB)) {
681 match_state_t *match;
684 mark = heap_pool_mark(&ctx->tmp_heap);
685 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, str->str);
687 heap_pool_clear(mark);
688 return E_OUTOFMEMORY;
691 hres = regexp_match_next(ctx, ®exp->dispex, 0, str, &match);
693 heap_pool_clear(mark);
701 hres = create_match_array(ctx, str, match, &ret);
703 *r = jsval_disp(ret);
709 heap_pool_clear(mark);
713 hres = regexp_match(ctx, ®exp->dispex, str, FALSE, &match_result, &match_cnt);
725 hres = create_array(ctx, match_cnt, &array);
729 for(i=0; i < match_cnt; i++) {
732 tmp_str = jsstr_substr(str, match_result[i].index, match_result[i].length);
734 hres = E_OUTOFMEMORY;
738 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
739 jsstr_release(tmp_str);
744 while(SUCCEEDED(hres)) {
745 hres = jsdisp_propput_name(array, indexW, jsval_number(match_result[match_cnt-1].index));
749 hres = jsdisp_propput_name(array, lastIndexW,
750 jsval_number(match_result[match_cnt-1].index + match_result[match_cnt-1].length));
754 hres = jsdisp_propput_name(array, inputW, jsval_string(str));
758 heap_free(match_result);
760 if(SUCCEEDED(hres) && r)
761 *r = jsval_obj(array);
763 jsdisp_release(array);
767 static HRESULT global_idx(script_ctx_t *ctx, DWORD flags, DWORD idx, jsval_t *r)
770 case DISPATCH_PROPERTYGET: {
773 ret = jsstr_substr(ctx->last_match, ctx->match_parens[idx].index, ctx->match_parens[idx].length);
775 return E_OUTOFMEMORY;
777 *r = jsval_string(ret);
780 case DISPATCH_PROPERTYPUT:
783 FIXME("unsupported flags\n");
790 static HRESULT RegExpConstr_idx1(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
791 unsigned argc, jsval_t *argv, jsval_t *r)
794 return global_idx(ctx, flags, 0, r);
797 static HRESULT RegExpConstr_idx2(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
798 unsigned argc, jsval_t *argv, jsval_t *r)
801 return global_idx(ctx, flags, 1, r);
804 static HRESULT RegExpConstr_idx3(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
805 unsigned argc, jsval_t *argv, jsval_t *r)
808 return global_idx(ctx, flags, 2, r);
811 static HRESULT RegExpConstr_idx4(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
812 unsigned argc, jsval_t *argv, jsval_t *r)
815 return global_idx(ctx, flags, 3, r);
818 static HRESULT RegExpConstr_idx5(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
819 unsigned argc, jsval_t *argv, jsval_t *r)
822 return global_idx(ctx, flags, 4, r);
825 static HRESULT RegExpConstr_idx6(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
826 unsigned argc, jsval_t *argv, jsval_t *r)
829 return global_idx(ctx, flags, 5, r);
832 static HRESULT RegExpConstr_idx7(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
833 unsigned argc, jsval_t *argv, jsval_t *r)
836 return global_idx(ctx, flags, 6, r);
839 static HRESULT RegExpConstr_idx8(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
840 unsigned argc, jsval_t *argv, jsval_t *r)
843 return global_idx(ctx, flags, 7, r);
846 static HRESULT RegExpConstr_idx9(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
847 unsigned argc, jsval_t *argv, jsval_t *r)
850 return global_idx(ctx, flags, 8, r);
853 static HRESULT RegExpConstr_leftContext(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
854 unsigned argc, jsval_t *argv, jsval_t *r)
859 case DISPATCH_PROPERTYGET: {
862 ret = jsstr_substr(ctx->last_match, 0, ctx->last_match_index);
864 return E_OUTOFMEMORY;
866 *r = jsval_string(ret);
869 case DISPATCH_PROPERTYPUT:
872 FIXME("unsupported flags\n");
879 static HRESULT RegExpConstr_rightContext(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
880 unsigned argc, jsval_t *argv, jsval_t *r)
885 case DISPATCH_PROPERTYGET: {
888 ret = jsstr_substr(ctx->last_match, ctx->last_match_index+ctx->last_match_length,
889 jsstr_length(ctx->last_match) - ctx->last_match_index - ctx->last_match_length);
891 return E_OUTOFMEMORY;
893 *r = jsval_string(ret);
896 case DISPATCH_PROPERTYPUT:
899 FIXME("unsupported flags\n");
906 static HRESULT RegExpConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
912 case DISPATCH_METHOD:
914 if(is_object_instance(argv[0])) {
915 jsdisp_t *jsdisp = iface_to_jsdisp((IUnknown*)get_object(argv[0]));
917 if(is_class(jsdisp, JSCLASS_REGEXP)) {
918 if(argc > 1 && !is_undefined(argv[1])) {
919 jsdisp_release(jsdisp);
920 return throw_regexp_error(ctx, JS_E_REGEXP_SYNTAX, NULL);
924 *r = jsval_obj(jsdisp);
926 jsdisp_release(jsdisp);
929 jsdisp_release(jsdisp);
934 case DISPATCH_CONSTRUCT: {
943 hres = create_regexp_var(ctx, argv[0], argc > 1 ? argv+1 : NULL, &ret);
954 FIXME("unimplemented flags: %x\n", flags);
961 static const builtin_prop_t RegExpConstr_props[] = {
962 {idx1W, RegExpConstr_idx1, 0},
963 {idx2W, RegExpConstr_idx2, 0},
964 {idx3W, RegExpConstr_idx3, 0},
965 {idx4W, RegExpConstr_idx4, 0},
966 {idx5W, RegExpConstr_idx5, 0},
967 {idx6W, RegExpConstr_idx6, 0},
968 {idx7W, RegExpConstr_idx7, 0},
969 {idx8W, RegExpConstr_idx8, 0},
970 {idx9W, RegExpConstr_idx9, 0},
971 {leftContextW, RegExpConstr_leftContext, 0},
972 {rightContextW, RegExpConstr_rightContext, 0}
975 static const builtin_info_t RegExpConstr_info = {
977 {NULL, Function_value, 0},
978 sizeof(RegExpConstr_props)/sizeof(*RegExpConstr_props),
984 HRESULT create_regexp_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
986 RegExpInstance *regexp;
989 static const WCHAR RegExpW[] = {'R','e','g','E','x','p',0};
991 hres = alloc_regexp(ctx, object_prototype, ®exp);
995 hres = create_builtin_constructor(ctx, RegExpConstr_value, RegExpW, &RegExpConstr_info,
996 PROPF_CONSTR|2, ®exp->dispex, ret);
998 jsdisp_release(®exp->dispex);
1002 HRESULT parse_regexp_flags(const WCHAR *str, DWORD str_len, DWORD *ret)
1007 for (p = str; p < str+str_len; p++) {
1016 flags |= REG_MULTILINE;
1019 flags |= REG_STICKY;
1022 WARN("wrong flag %c\n", *p);