зеркало из https://github.com/github/ruby.git
* parse.y (str_extend): should check nesting parentheses in #{}.
* process.c (pst_wstopsig): returns nil unless WIFSTOPPED() is non-zero. * process.c (pst_wtermsig): returns nil unless WIFSIGNALED() is non-zero. * process.c (pst_wexitstatus): returns nil unless WIFEXITED() is non-zero. * eval.c (rb_thread_select): tv_sec and tv_usec should not be negative. * signal.c (posix_signal): do not set SA_RESTART for SIGVTALRM. * parse.y (call_args2): block_arg may follow the first argument in call_args2. * eval.c (stack_check): should avoid stack length check during raising SystemStackError exception. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1852 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
e15f65b004
Коммит
f1fdbf080e
32
ChangeLog
32
ChangeLog
|
@ -1,8 +1,40 @@
|
|||
Thu Nov 22 00:28:13 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||
|
||||
* parse.y (str_extend): should check nesting parentheses in #{}.
|
||||
|
||||
Wed Nov 21 12:22:52 2001 Shugo Maeda <shugo@ruby-lang.org>
|
||||
|
||||
* lib/cgi.rb: CGI#header: do not set Apache.request.status for
|
||||
Location: if Apache.request.status is already set.
|
||||
|
||||
Wed Nov 21 02:24:18 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||
|
||||
* process.c (pst_wstopsig): returns nil unless WIFSTOPPED() is
|
||||
non-zero.
|
||||
|
||||
* process.c (pst_wtermsig): returns nil unless WIFSIGNALED() is
|
||||
non-zero.
|
||||
|
||||
* process.c (pst_wexitstatus): returns nil unless WIFEXITED() is
|
||||
non-zero.
|
||||
|
||||
Wed Nov 21 00:17:54 2001 Ville Mattila <mulperi@iki.fi>
|
||||
|
||||
* eval.c (rb_thread_select): tv_sec and tv_usec should not be
|
||||
negative.
|
||||
|
||||
* signal.c (posix_signal): do not set SA_RESTART for SIGVTALRM.
|
||||
|
||||
Tue Nov 20 21:09:22 2001 Guy Decoux <ts@moulon.inra.fr>
|
||||
|
||||
* parse.y (call_args2): block_arg may follow the first argument in
|
||||
call_args2.
|
||||
|
||||
Tue Nov 20 02:01:15 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||
|
||||
* eval.c (stack_check): should avoid stack length check during
|
||||
raising SystemStackError exception.
|
||||
|
||||
Tue Nov 20 01:07:13 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||
|
||||
* parse.y (str_extend): should not terminate string interpolation
|
||||
|
|
26
eval.c
26
eval.c
|
@ -496,6 +496,7 @@ extern NODE *ruby_eval_tree;
|
|||
extern int ruby_nerrs;
|
||||
|
||||
static VALUE rb_eLocalJumpError;
|
||||
static VALUE rb_eSysStackError;
|
||||
|
||||
extern VALUE ruby_top_self;
|
||||
|
||||
|
@ -4135,6 +4136,24 @@ rb_with_disable_interrupt(proc, data)
|
|||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
stack_check()
|
||||
{
|
||||
static int overflowing = 0;
|
||||
|
||||
if (!overflowing && ruby_stack_check()) {
|
||||
int state;
|
||||
overflowing = 1;
|
||||
PUSH_TAG(PROT_NONE);
|
||||
if ((state = EXEC_TAG()) == 0) {
|
||||
rb_raise(rb_eSysStackError, "stack level too deep");
|
||||
}
|
||||
POP_TAG();
|
||||
overflowing = 0;
|
||||
JUMP_TAG(state);
|
||||
}
|
||||
}
|
||||
|
||||
static int last_call_status;
|
||||
|
||||
#define CSTAT_PRIV 1
|
||||
|
@ -4159,7 +4178,7 @@ rb_f_missing(argc, argv, obj)
|
|||
rb_raise(rb_eArgError, "no id given");
|
||||
}
|
||||
|
||||
ruby_stack_check();
|
||||
stack_check();
|
||||
|
||||
id = SYM2ID(argv[0]);
|
||||
|
||||
|
@ -4365,7 +4384,7 @@ rb_call0(klass, recv, id, argc, argv, body, nosuper)
|
|||
|
||||
if ((++tick & 0xff) == 0) {
|
||||
CHECK_INTS; /* better than nothing */
|
||||
ruby_stack_check();
|
||||
stack_check();
|
||||
}
|
||||
PUSH_ITER(itr);
|
||||
PUSH_FRAME();
|
||||
|
@ -6956,6 +6975,7 @@ void
|
|||
Init_Proc()
|
||||
{
|
||||
rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError);
|
||||
rb_eSysStackError = rb_define_class("SystemStackError", rb_eStandardError);
|
||||
|
||||
rb_cProc = rb_define_class("Proc", rb_cObject);
|
||||
rb_undef_method(CLASS_OF(rb_cProc), "allocate");
|
||||
|
@ -7881,6 +7901,8 @@ rb_thread_select(max, read, write, except, timeout)
|
|||
|
||||
tv.tv_sec = (unsigned int)d;
|
||||
tv.tv_usec = (long)((d-(double)tv.tv_sec)*1e6);
|
||||
if (tv.tv_sec < 0) tv.tv_sec = 0;
|
||||
if (tv.tv_usec < 0) tv.tv_usec = 0;
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
|
|
|
@ -4,6 +4,9 @@ if /mswin32|mingw/ !~ RUBY_PLATFORM
|
|||
have_header("sys/stropts.h")
|
||||
have_func("setresuid")
|
||||
$CFLAGS << "-DHAVE_DEV_PTMX" if /cygwin/ === RUBY_PLATFORM
|
||||
have_header("libutil.h")
|
||||
have_header("pty.h")
|
||||
have_library("util", "openpty")
|
||||
if have_func("openpty") or
|
||||
have_func("_getpty") or
|
||||
have_func("ioctl")
|
||||
|
|
111
ext/pty/pty.c
111
ext/pty/pty.c
|
@ -9,6 +9,12 @@
|
|||
#if !defined(HAVE_OPENPTY) && !defined(HAVE__GETPTY)
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIBUTIL_H
|
||||
#include <libutil.h>
|
||||
#endif
|
||||
#ifdef HAVE_PTY_H
|
||||
#include <pty.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#include <sys/wait.h>
|
||||
#else
|
||||
|
@ -88,6 +94,8 @@ char *MasterDevice = "/dev/pty%s",
|
|||
"q8","q9","qa","qb","qc","qd","qe","qf",
|
||||
"r0","r1","r2","r3","r4","r5","r6","r7",
|
||||
"r8","r9","ra","rb","rc","rd","re","rf",
|
||||
"s0","s1","s2","s3","s4","s5","s6","s7",
|
||||
"s8","s9","sa","sb","sc","sd","se","sf",
|
||||
0,
|
||||
};
|
||||
#endif /* _IBMESA */
|
||||
|
@ -110,56 +118,75 @@ extern int errno;
|
|||
# endif /* HAVE_SETREUID */
|
||||
#endif /* NO_SETEUID */
|
||||
|
||||
static VALUE eChildExited;
|
||||
|
||||
static VALUE
|
||||
echild_status(self)
|
||||
VALUE self;
|
||||
{
|
||||
return rb_ivar_get(self, rb_intern("status"));
|
||||
}
|
||||
|
||||
struct pty_info {
|
||||
int fd;
|
||||
pid_t child_pid;
|
||||
VALUE thread;
|
||||
};
|
||||
|
||||
static void
|
||||
pty_raise(thread, cpid, stop)
|
||||
VALUE thread;
|
||||
int cpid;
|
||||
int stop;
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf), "pty - %s: %d", stop ? "stopped" : "changed", cpid);
|
||||
rb_funcall(thread, rb_intern("raise"), 1, rb_str_new2(buf));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
pty_syswait(info)
|
||||
struct pty_info *info;
|
||||
{
|
||||
extern VALUE rb_last_status;
|
||||
int cpid, status;
|
||||
char buf[1024];
|
||||
VALUE exc, st;
|
||||
char *state = "changed";
|
||||
|
||||
cpid = rb_waitpid(info->child_pid, &status, WUNTRACED);
|
||||
printf("cpid: %d (%d)\n", cpid, status);
|
||||
st = rb_last_status;
|
||||
|
||||
if (cpid == 0 || cpid == -1)
|
||||
return Qnil;
|
||||
|
||||
#ifdef IF_STOPPED
|
||||
if (IF_STOPPED(status)) { /* suspend */
|
||||
pty_raise(info->thread, cpid, Qtrue);
|
||||
state = "stopped";
|
||||
}
|
||||
#else
|
||||
#ifdef WIFSTOPPED
|
||||
if (WIFSTOPPED(status)) { /* suspend */
|
||||
pty_raise(info->thread, cpid, Qtrue);
|
||||
state = "stopped";
|
||||
}
|
||||
#else
|
||||
---->> Either IF_STOPPED or WIFSTOPPED is needed <<----
|
||||
#endif /* WIFSTOPPED */
|
||||
#endif /* IF_STOPPED */
|
||||
if (WIFEXITED(status)) {
|
||||
state = "exit";
|
||||
}
|
||||
|
||||
pty_raise(info->thread, cpid, Qfalse);
|
||||
snprintf(buf, sizeof(buf), "pty - %s: %d", state, cpid);
|
||||
exc = rb_exc_new2(eChildExited, buf);
|
||||
rb_iv_set(exc, "status", st);
|
||||
rb_funcall(info->thread, rb_intern("raise"), 1, exc);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static void getDevice _((int*, int*));
|
||||
|
||||
struct exec_info {
|
||||
int argc;
|
||||
VALUE *argv;
|
||||
};
|
||||
|
||||
static VALUE
|
||||
pty_exec(arg)
|
||||
struct exec_info *arg;
|
||||
{
|
||||
return rb_f_exec(arg->argc, arg->argv);
|
||||
}
|
||||
|
||||
static void
|
||||
establishShell(argc, argv, info)
|
||||
int argc;
|
||||
|
@ -169,7 +196,9 @@ establishShell(argc, argv, info)
|
|||
static int i,master,slave,currentPid;
|
||||
char *p,*getenv();
|
||||
struct passwd *pwent;
|
||||
VALUE v;
|
||||
VALUE v;
|
||||
struct exec_info arg;
|
||||
int status;
|
||||
|
||||
if (argc == 0) {
|
||||
char *shellname;
|
||||
|
@ -190,14 +219,13 @@ establishShell(argc, argv, info)
|
|||
}
|
||||
getDevice(&master,&slave);
|
||||
|
||||
info->thread = rb_thread_current();
|
||||
currentPid = getpid();
|
||||
if((i = fork()) < 0) {
|
||||
rb_sys_fail("fork failed");
|
||||
}
|
||||
|
||||
if(i == 0) { /* child */
|
||||
/* int argc;
|
||||
char *argv[1024]; */
|
||||
currentPid = getpid();
|
||||
|
||||
/*
|
||||
|
@ -248,7 +276,9 @@ establishShell(argc, argv, info)
|
|||
seteuid(getuid());
|
||||
#endif
|
||||
|
||||
rb_f_exec(argc, argv);
|
||||
arg.argc = argc;
|
||||
arg.argv = argv;
|
||||
rb_protect(pty_exec, (VALUE)&arg, &status);
|
||||
sleep(1);
|
||||
_exit(1);
|
||||
}
|
||||
|
@ -259,6 +289,28 @@ establishShell(argc, argv, info)
|
|||
info->fd = master;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
pty_kill_child(info)
|
||||
struct pty_info *info;
|
||||
{
|
||||
if (rb_funcall(info->thread, rb_intern("alive?"), 0, 0) == Qtrue &&
|
||||
kill(info->child_pid, 0) == 0) {
|
||||
rb_thread_schedule();
|
||||
if (kill(info->child_pid, SIGTERM) == 0) {
|
||||
rb_thread_schedule();
|
||||
if (kill(info->child_pid, 0) == 0) {
|
||||
kill(info->child_pid, SIGINT);
|
||||
rb_thread_schedule();
|
||||
if (kill(info->child_pid, 0) == 0)
|
||||
kill(info->child_pid, SIGKILL);
|
||||
}
|
||||
}
|
||||
}
|
||||
rb_funcall(info->thread, rb_intern("join"), 0, 0);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_OPENPTY
|
||||
/*
|
||||
* Use openpty(3) of 4.3BSD Reno and later,
|
||||
|
@ -346,7 +398,7 @@ getDevice(master,slave)
|
|||
close(i);
|
||||
}
|
||||
}
|
||||
rb_raise(rb_eRuntimeError, "Cannot get %s\n", SlaveDevice);
|
||||
rb_raise(rb_eRuntimeError, "Cannot get %s", SlaveName);
|
||||
#endif
|
||||
}
|
||||
#endif /* HAVE__GETPTY */
|
||||
|
@ -367,7 +419,7 @@ pty_getpty(argc, argv, self)
|
|||
VALUE self;
|
||||
{
|
||||
VALUE res, th;
|
||||
struct pty_info info;
|
||||
struct pty_info info, thinfo;
|
||||
OpenFile *wfptr,*rfptr;
|
||||
VALUE rport = rb_obj_alloc(rb_cFile);
|
||||
VALUE wport = rb_obj_alloc(rb_cFile);
|
||||
|
@ -379,22 +431,24 @@ pty_getpty(argc, argv, self)
|
|||
|
||||
rfptr->mode = rb_io_mode_flags("r");
|
||||
rfptr->f = fdopen(info.fd, "r");
|
||||
rfptr->path = 0; /*strdup(RSTRING(command)->ptr); */
|
||||
rfptr->path = strdup(SlaveName);
|
||||
|
||||
wfptr->mode = rb_io_mode_flags("w");
|
||||
wfptr->f = fdopen(dup(info.fd), "w");
|
||||
wfptr->path = 0; /* strdup(RSTRING(command)->ptr); */
|
||||
wfptr->path = strdup(SlaveName);
|
||||
|
||||
res = rb_ary_new2(3);
|
||||
rb_ary_store(res,0,(VALUE)rport);
|
||||
rb_ary_store(res,1,(VALUE)wport);
|
||||
rb_ary_store(res,2,INT2FIX(info.child_pid));
|
||||
|
||||
info.thread = rb_thread_current();
|
||||
th = rb_thread_create(pty_syswait, (void*)&info);
|
||||
thinfo.thread = th;
|
||||
thinfo.child_pid = info.child_pid;
|
||||
|
||||
if (rb_block_given_p()) {
|
||||
res = rb_yield((VALUE)res);
|
||||
rb_funcall(th, rb_intern("kill"), 0, 0);
|
||||
rb_ensure(rb_yield, res, pty_kill_child, (VALUE)&thinfo);
|
||||
return Qnil;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -428,4 +482,7 @@ Init_pty()
|
|||
rb_define_module_function(cPTY,"spawn",pty_getpty,-1);
|
||||
rb_define_module_function(cPTY,"protect_signal",pty_protect,0);
|
||||
rb_define_module_function(cPTY,"reset_signal",pty_reset_signal,0);
|
||||
|
||||
eChildExited = rb_define_class_under(cPTY,"ChildExited",rb_eRuntimeError);
|
||||
rb_define_method(eChildExited,"status",echild_status,0);
|
||||
}
|
||||
|
|
10
gc.c
10
gc.c
|
@ -404,17 +404,13 @@ ruby_stack_length(p)
|
|||
return STACK_LENGTH;
|
||||
}
|
||||
|
||||
static VALUE rb_eSysStackError;
|
||||
|
||||
void
|
||||
int
|
||||
ruby_stack_check()
|
||||
{
|
||||
int ret;
|
||||
|
||||
CHECK_STACK(ret);
|
||||
if (ret) {
|
||||
rb_raise(rb_eSysStackError, "stack level too deep");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MARK_STACK_MAX 1024
|
||||
|
@ -1522,6 +1518,4 @@ Init_GC()
|
|||
rb_global_variable(&finalizers);
|
||||
rb_gc_unregister_address(&rb_mObSpace);
|
||||
finalizers = rb_ary_new();
|
||||
|
||||
rb_eSysStackError = rb_define_class("SystemStackError", rb_eStandardError);
|
||||
}
|
||||
|
|
2
intern.h
2
intern.h
|
@ -189,7 +189,7 @@ void rb_file_const _((const char*, VALUE));
|
|||
int rb_find_file_ext _((VALUE*, const char* const*));
|
||||
VALUE rb_find_file _((VALUE));
|
||||
/* gc.c */
|
||||
void ruby_stack_check _((void));
|
||||
int ruby_stack_check _((void));
|
||||
int ruby_stack_length _((VALUE**));
|
||||
void rb_gc_mark_locations _((VALUE*, VALUE*));
|
||||
void rb_mark_tbl _((struct st_table*));
|
||||
|
|
|
@ -76,7 +76,7 @@ SimpleDelegater = SimpleDelegator
|
|||
#
|
||||
def DelegateClass(superclass)
|
||||
klass = Class.new
|
||||
methods = superclass.instance_methods
|
||||
methods = superclass.instance_methods(true)
|
||||
methods -= ::Kernel.instance_methods
|
||||
methods |= ["to_s","to_a","inspect","==","=~","==="]
|
||||
klass.module_eval <<-EOS
|
||||
|
|
32
parse.y
32
parse.y
|
@ -1104,6 +1104,10 @@ call_args2 : arg ',' args opt_block_arg
|
|||
{
|
||||
$$ = arg_blk_pass(list_concat(NEW_LIST($1),$3), $4);
|
||||
}
|
||||
| arg ',' block_arg
|
||||
{
|
||||
$$ = arg_blk_pass($1, $3);
|
||||
}
|
||||
| arg ',' tSTAR arg opt_block_arg
|
||||
{
|
||||
value_expr($1);
|
||||
|
@ -2044,7 +2048,7 @@ none : /* none */
|
|||
static char *tokenbuf = NULL;
|
||||
static int tokidx, toksiz = 0;
|
||||
|
||||
static NODE *str_extend();
|
||||
static NODE *str_extend _((NODE*,char,char));
|
||||
|
||||
#define LEAVE_BS 1
|
||||
|
||||
|
@ -2513,7 +2517,7 @@ parse_regx(term, paren)
|
|||
|
||||
switch (c) {
|
||||
case '#':
|
||||
list = str_extend(list, term);
|
||||
list = str_extend(list, term, paren);
|
||||
if (list == (NODE*)-1) goto unterminated;
|
||||
continue;
|
||||
|
||||
|
@ -2642,7 +2646,7 @@ parse_string(func, term, paren)
|
|||
}
|
||||
}
|
||||
else if (c == '#') {
|
||||
list = str_extend(list, term);
|
||||
list = str_extend(list, term, paren);
|
||||
if (list == (NODE*)-1) goto unterm_str;
|
||||
continue;
|
||||
}
|
||||
|
@ -3873,15 +3877,16 @@ yylex()
|
|||
}
|
||||
|
||||
static NODE*
|
||||
str_extend(list, term)
|
||||
str_extend(list, term, paren)
|
||||
NODE *list;
|
||||
char term;
|
||||
char term, paren;
|
||||
{
|
||||
int c;
|
||||
int brace = -1;
|
||||
VALUE ss;
|
||||
NODE *node;
|
||||
int nest;
|
||||
int brace_nest = 0;
|
||||
int paren_nest = 0;
|
||||
|
||||
c = nextc();
|
||||
switch (c) {
|
||||
|
@ -3996,13 +4001,13 @@ str_extend(list, term)
|
|||
|
||||
case '{':
|
||||
if (c == '{') brace = '}';
|
||||
nest = 0;
|
||||
brace_nest = 0;
|
||||
do {
|
||||
loop_again:
|
||||
c = nextc();
|
||||
switch (c) {
|
||||
case -1:
|
||||
if (nest > 0) {
|
||||
if (brace_nest > 0) {
|
||||
yyerror("bad substitution in string");
|
||||
newtok();
|
||||
return list;
|
||||
|
@ -4010,8 +4015,8 @@ str_extend(list, term)
|
|||
return (NODE*)-1;
|
||||
case '}':
|
||||
if (c == brace) {
|
||||
if (nest == 0) break;
|
||||
nest--;
|
||||
if (brace_nest == 0) break;
|
||||
brace_nest--;
|
||||
}
|
||||
tokadd(c);
|
||||
goto loop_again;
|
||||
|
@ -4027,9 +4032,10 @@ str_extend(list, term)
|
|||
}
|
||||
break;
|
||||
case '{':
|
||||
if (brace != -1) nest++;
|
||||
if (brace != -1) brace_nest++;
|
||||
default:
|
||||
if (c == term) {
|
||||
if (c == paren) paren_nest++;
|
||||
else if (c == term && (!paren || paren_nest-- == 0)) {
|
||||
pushback(c);
|
||||
list_append(list, NEW_STR(rb_str_new2("#")));
|
||||
rb_warn("bad substitution in string");
|
||||
|
@ -4282,7 +4288,7 @@ gettable(id)
|
|||
if (in_single) return NEW_CVAR2(id);
|
||||
return NEW_CVAR(id);
|
||||
}
|
||||
rb_bug("invalid id for gettable");
|
||||
rb_compile_error("identifier %s is not valid", rb_id2name(id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
12
process.c
12
process.c
|
@ -161,7 +161,9 @@ pst_wstopsig(st)
|
|||
{
|
||||
int status = NUM2INT(st);
|
||||
|
||||
return INT2NUM(WSTOPSIG(status));
|
||||
if (WIFSTOPPED(status))
|
||||
return INT2NUM(WSTOPSIG(status));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
@ -182,7 +184,9 @@ pst_wtermsig(st)
|
|||
{
|
||||
int status = NUM2INT(st);
|
||||
|
||||
return INT2NUM(WTERMSIG(status));
|
||||
if (WIFSIGNALED(status))
|
||||
return INT2NUM(WTERMSIG(status));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
@ -203,7 +207,9 @@ pst_wexitstatus(st)
|
|||
{
|
||||
int status = NUM2INT(st);
|
||||
|
||||
return INT2NUM(WEXITSTATUS(status));
|
||||
if (WIFEXITED(status))
|
||||
return INT2NUM(WEXITSTATUS(status));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
|
7
signal.c
7
signal.c
|
@ -293,7 +293,12 @@ posix_signal(signum, handler)
|
|||
sigemptyset(&sigact.sa_mask);
|
||||
sigact.sa_flags = 0;
|
||||
#if defined(SA_RESTART)
|
||||
sigact.sa_flags |= SA_RESTART; /* SVR4, 4.3+BSD */
|
||||
/* All other signals but VTALRM shall restart restartable syscall
|
||||
VTALRM will cause EINTR to syscall if interrupted.
|
||||
*/
|
||||
if (signum != SIGVTALRM) {
|
||||
sigact.sa_flags |= SA_RESTART; /* SVR4, 4.3+BSD */
|
||||
}
|
||||
#endif
|
||||
#ifdef SA_NOCLDWAIT
|
||||
if (signum == SIGCHLD && handler == SIG_IGN)
|
||||
|
|
Загрузка…
Ссылка в новой задаче