annotate liboffloadmic/runtime/offload_env.cpp @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 04ced10e8804
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
111
kono
parents:
diff changeset
1 /*
kono
parents:
diff changeset
2 Copyright (c) 2014-2016 Intel Corporation. All Rights Reserved.
kono
parents:
diff changeset
3
kono
parents:
diff changeset
4 Redistribution and use in source and binary forms, with or without
kono
parents:
diff changeset
5 modification, are permitted provided that the following conditions
kono
parents:
diff changeset
6 are met:
kono
parents:
diff changeset
7
kono
parents:
diff changeset
8 * Redistributions of source code must retain the above copyright
kono
parents:
diff changeset
9 notice, this list of conditions and the following disclaimer.
kono
parents:
diff changeset
10 * Redistributions in binary form must reproduce the above copyright
kono
parents:
diff changeset
11 notice, this list of conditions and the following disclaimer in the
kono
parents:
diff changeset
12 documentation and/or other materials provided with the distribution.
kono
parents:
diff changeset
13 * Neither the name of Intel Corporation nor the names of its
kono
parents:
diff changeset
14 contributors may be used to endorse or promote products derived
kono
parents:
diff changeset
15 from this software without specific prior written permission.
kono
parents:
diff changeset
16
kono
parents:
diff changeset
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
kono
parents:
diff changeset
18 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
kono
parents:
diff changeset
19 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
kono
parents:
diff changeset
20 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
kono
parents:
diff changeset
21 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
kono
parents:
diff changeset
22 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
kono
parents:
diff changeset
23 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
kono
parents:
diff changeset
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
kono
parents:
diff changeset
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
kono
parents:
diff changeset
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
kono
parents:
diff changeset
27 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
kono
parents:
diff changeset
28 */
kono
parents:
diff changeset
29
kono
parents:
diff changeset
30
kono
parents:
diff changeset
31 #include "offload_env.h"
kono
parents:
diff changeset
32 #include <string.h>
kono
parents:
diff changeset
33 #include <ctype.h>
kono
parents:
diff changeset
34 #include "offload_util.h"
kono
parents:
diff changeset
35 #include "liboffload_error_codes.h"
kono
parents:
diff changeset
36
kono
parents:
diff changeset
37 // for environment variables valid on all cards
kono
parents:
diff changeset
38 const int MicEnvVar::any_card = -1;
kono
parents:
diff changeset
39
kono
parents:
diff changeset
40 MicEnvVar::~MicEnvVar()
kono
parents:
diff changeset
41 {
kono
parents:
diff changeset
42 for (std::list<MicEnvVar::CardEnvVars*>::const_iterator
kono
parents:
diff changeset
43 it = card_spec_list.begin();
kono
parents:
diff changeset
44 it != card_spec_list.end(); it++) {
kono
parents:
diff changeset
45 CardEnvVars *card_data = *it;
kono
parents:
diff changeset
46 delete card_data;
kono
parents:
diff changeset
47 }
kono
parents:
diff changeset
48 }
kono
parents:
diff changeset
49
kono
parents:
diff changeset
50 MicEnvVar::VarValue::~VarValue()
kono
parents:
diff changeset
51 {
kono
parents:
diff changeset
52 free(env_var_value);
kono
parents:
diff changeset
53 }
kono
parents:
diff changeset
54
kono
parents:
diff changeset
55 MicEnvVar::CardEnvVars::~CardEnvVars()
kono
parents:
diff changeset
56 {
kono
parents:
diff changeset
57 for (std::list<MicEnvVar::VarValue*>::const_iterator it = env_vars.begin();
kono
parents:
diff changeset
58 it != env_vars.end(); it++) {
kono
parents:
diff changeset
59 VarValue *var_value = *it;
kono
parents:
diff changeset
60 delete var_value;
kono
parents:
diff changeset
61 }
kono
parents:
diff changeset
62 }
kono
parents:
diff changeset
63
kono
parents:
diff changeset
64 // Searching for card in "card_spec_list" list with the same "number"
kono
parents:
diff changeset
65
kono
parents:
diff changeset
66 MicEnvVar::CardEnvVars* MicEnvVar::get_card(int number)
kono
parents:
diff changeset
67 {
kono
parents:
diff changeset
68 if (number == any_card) {
kono
parents:
diff changeset
69 return &common_vars;
kono
parents:
diff changeset
70 }
kono
parents:
diff changeset
71 for (std::list<MicEnvVar::CardEnvVars*>::const_iterator
kono
parents:
diff changeset
72 it = card_spec_list.begin();
kono
parents:
diff changeset
73 it != card_spec_list.end(); it++) {
kono
parents:
diff changeset
74 CardEnvVars *card_data = *it;
kono
parents:
diff changeset
75 if (card_data->card_number == number) {
kono
parents:
diff changeset
76 return card_data;
kono
parents:
diff changeset
77 }
kono
parents:
diff changeset
78 }
kono
parents:
diff changeset
79 return NULL;
kono
parents:
diff changeset
80 }
kono
parents:
diff changeset
81
kono
parents:
diff changeset
82 // Searching for environment variable in "env_var" list with the same name
kono
parents:
diff changeset
83
kono
parents:
diff changeset
84 MicEnvVar::VarValue* MicEnvVar::CardEnvVars::find_var(
kono
parents:
diff changeset
85 char* env_var_name,
kono
parents:
diff changeset
86 int env_var_name_length
kono
parents:
diff changeset
87 )
kono
parents:
diff changeset
88 {
kono
parents:
diff changeset
89 for (std::list<MicEnvVar::VarValue*>::const_iterator it = env_vars.begin();
kono
parents:
diff changeset
90 it != env_vars.end(); it++) {
kono
parents:
diff changeset
91 VarValue *var_value = *it;
kono
parents:
diff changeset
92 if (var_value->length == env_var_name_length &&
kono
parents:
diff changeset
93 !strncmp(var_value->env_var, env_var_name,
kono
parents:
diff changeset
94 env_var_name_length)) {
kono
parents:
diff changeset
95 return var_value;
kono
parents:
diff changeset
96 }
kono
parents:
diff changeset
97 }
kono
parents:
diff changeset
98 return NULL;
kono
parents:
diff changeset
99 }
kono
parents:
diff changeset
100
kono
parents:
diff changeset
101 void MicEnvVar::analyze_env_var(char *env_var_string)
kono
parents:
diff changeset
102 {
kono
parents:
diff changeset
103 char *env_var_name;
kono
parents:
diff changeset
104 char *env_var_def;
kono
parents:
diff changeset
105 int card_number;
kono
parents:
diff changeset
106 int env_var_name_length;
kono
parents:
diff changeset
107 MicEnvVarKind env_var_kind;
kono
parents:
diff changeset
108
kono
parents:
diff changeset
109 env_var_kind = get_env_var_kind(env_var_string,
kono
parents:
diff changeset
110 &card_number,
kono
parents:
diff changeset
111 &env_var_name,
kono
parents:
diff changeset
112 &env_var_name_length,
kono
parents:
diff changeset
113 &env_var_def);
kono
parents:
diff changeset
114 switch (env_var_kind) {
kono
parents:
diff changeset
115 case c_mic_var:
kono
parents:
diff changeset
116 case c_mic_card_var:
kono
parents:
diff changeset
117 add_env_var(card_number,
kono
parents:
diff changeset
118 env_var_name,
kono
parents:
diff changeset
119 env_var_name_length,
kono
parents:
diff changeset
120 env_var_def);
kono
parents:
diff changeset
121 break;
kono
parents:
diff changeset
122 case c_mic_card_env:
kono
parents:
diff changeset
123 mic_parse_env_var_list(card_number, env_var_def);
kono
parents:
diff changeset
124 break;
kono
parents:
diff changeset
125 case c_no_mic:
kono
parents:
diff changeset
126 default:
kono
parents:
diff changeset
127 break;
kono
parents:
diff changeset
128 }
kono
parents:
diff changeset
129 }
kono
parents:
diff changeset
130
kono
parents:
diff changeset
131 void MicEnvVar::add_env_var(
kono
parents:
diff changeset
132 int card_number,
kono
parents:
diff changeset
133 char *env_var_name,
kono
parents:
diff changeset
134 int env_var_name_length,
kono
parents:
diff changeset
135 char *env_var_def
kono
parents:
diff changeset
136 )
kono
parents:
diff changeset
137 {
kono
parents:
diff changeset
138 VarValue *var;
kono
parents:
diff changeset
139 CardEnvVars *card;
kono
parents:
diff changeset
140
kono
parents:
diff changeset
141 // The case corresponds to common env var definition of kind
kono
parents:
diff changeset
142 // <mic-prefix>_<var>
kono
parents:
diff changeset
143 if (card_number == any_card) {
kono
parents:
diff changeset
144 card = &common_vars;
kono
parents:
diff changeset
145 }
kono
parents:
diff changeset
146 else {
kono
parents:
diff changeset
147 card = get_card(card_number);
kono
parents:
diff changeset
148 if (!card) {
kono
parents:
diff changeset
149 // definition for new card occurred
kono
parents:
diff changeset
150 card = new CardEnvVars(card_number);
kono
parents:
diff changeset
151 card_spec_list.push_back(card);
kono
parents:
diff changeset
152 }
kono
parents:
diff changeset
153
kono
parents:
diff changeset
154 }
kono
parents:
diff changeset
155 var = card->find_var(env_var_name, env_var_name_length);
kono
parents:
diff changeset
156 if (!var) {
kono
parents:
diff changeset
157 // put new env var definition in "env_var" list
kono
parents:
diff changeset
158 var = new VarValue(env_var_name, env_var_name_length, env_var_def);
kono
parents:
diff changeset
159 card->env_vars.push_back(var);
kono
parents:
diff changeset
160 }
kono
parents:
diff changeset
161 }
kono
parents:
diff changeset
162
kono
parents:
diff changeset
163 // The routine analyses string pointed by "env_var_string" argument
kono
parents:
diff changeset
164 // according to the following syntax:
kono
parents:
diff changeset
165 //
kono
parents:
diff changeset
166 // Specification of prefix for MIC environment variables
kono
parents:
diff changeset
167 // MIC_ENV_PREFIX=<mic-prefix>
kono
parents:
diff changeset
168 //
kono
parents:
diff changeset
169 // Setting single MIC environment variable
kono
parents:
diff changeset
170 // <mic-prefix>_<var>=<value>
kono
parents:
diff changeset
171 // <mic-prefix>_<card-number>_<var>=<value>
kono
parents:
diff changeset
172
kono
parents:
diff changeset
173 // Setting multiple MIC environment variables
kono
parents:
diff changeset
174 // <mic-prefix>_<card-number>_ENV=<env-vars>
kono
parents:
diff changeset
175
kono
parents:
diff changeset
176 MicEnvVarKind MicEnvVar::get_env_var_kind(
kono
parents:
diff changeset
177 char *env_var_string,
kono
parents:
diff changeset
178 int *card_number,
kono
parents:
diff changeset
179 char **env_var_name,
kono
parents:
diff changeset
180 int *env_var_name_length,
kono
parents:
diff changeset
181 char **env_var_def
kono
parents:
diff changeset
182 )
kono
parents:
diff changeset
183 {
kono
parents:
diff changeset
184 int len = strlen(prefix);
kono
parents:
diff changeset
185 char *c = env_var_string;
kono
parents:
diff changeset
186 int num = 0;
kono
parents:
diff changeset
187 bool card_is_set = false;
kono
parents:
diff changeset
188
kono
parents:
diff changeset
189 if (strncmp(c, prefix, len) != 0 || c[len] != '_') {
kono
parents:
diff changeset
190 return c_no_mic;
kono
parents:
diff changeset
191 }
kono
parents:
diff changeset
192 c += len + 1;
kono
parents:
diff changeset
193
kono
parents:
diff changeset
194 *card_number = any_card;
kono
parents:
diff changeset
195 if (isdigit(*c)) {
kono
parents:
diff changeset
196 while (isdigit (*c)) {
kono
parents:
diff changeset
197 num = (*c++ - '0') + (num * 10);
kono
parents:
diff changeset
198 }
kono
parents:
diff changeset
199 if (*c != '_') {
kono
parents:
diff changeset
200 return c_no_mic;
kono
parents:
diff changeset
201 }
kono
parents:
diff changeset
202 c++;
kono
parents:
diff changeset
203 *card_number = num;
kono
parents:
diff changeset
204 card_is_set = true;
kono
parents:
diff changeset
205 }
kono
parents:
diff changeset
206 if (!isalpha(*c)) {
kono
parents:
diff changeset
207 return c_no_mic;
kono
parents:
diff changeset
208 }
kono
parents:
diff changeset
209 *env_var_name = *env_var_def = c;
kono
parents:
diff changeset
210 if (strncmp(c, "ENV=", 4) == 0) {
kono
parents:
diff changeset
211 if (!card_is_set) {
kono
parents:
diff changeset
212 *env_var_name_length = 3;
kono
parents:
diff changeset
213 *env_var_name = *env_var_def = c;
kono
parents:
diff changeset
214 *env_var_def = strdup(*env_var_def);
kono
parents:
diff changeset
215 if (*env_var_def == NULL)
kono
parents:
diff changeset
216 LIBOFFLOAD_ERROR(c_malloc);
kono
parents:
diff changeset
217 return c_mic_var;
kono
parents:
diff changeset
218 }
kono
parents:
diff changeset
219 *env_var_def = c + strlen("ENV=");
kono
parents:
diff changeset
220 *env_var_def = strdup(*env_var_def);
kono
parents:
diff changeset
221 if (*env_var_def == NULL)
kono
parents:
diff changeset
222 LIBOFFLOAD_ERROR(c_malloc);
kono
parents:
diff changeset
223 return c_mic_card_env;
kono
parents:
diff changeset
224 }
kono
parents:
diff changeset
225 if (isalpha(*c)) {
kono
parents:
diff changeset
226 *env_var_name_length = 0;
kono
parents:
diff changeset
227 while (isalnum(*c) || *c == '_') {
kono
parents:
diff changeset
228 c++;
kono
parents:
diff changeset
229 (*env_var_name_length)++;
kono
parents:
diff changeset
230 }
kono
parents:
diff changeset
231 }
kono
parents:
diff changeset
232 if (*c != '=') {
kono
parents:
diff changeset
233 return c_no_mic;
kono
parents:
diff changeset
234 }
kono
parents:
diff changeset
235 *env_var_def = strdup(*env_var_def);
kono
parents:
diff changeset
236 if (*env_var_def == NULL)
kono
parents:
diff changeset
237 LIBOFFLOAD_ERROR(c_malloc);
kono
parents:
diff changeset
238 return card_is_set? c_mic_card_var : c_mic_var;
kono
parents:
diff changeset
239 }
kono
parents:
diff changeset
240
kono
parents:
diff changeset
241 // analysing <env-vars> in form:
kono
parents:
diff changeset
242 // <mic-prefix>_<card-number>_ENV=<env-vars>
kono
parents:
diff changeset
243 // where:
kono
parents:
diff changeset
244 //
kono
parents:
diff changeset
245 // <env-vars>:
kono
parents:
diff changeset
246 // <env-var>
kono
parents:
diff changeset
247 // <env-vars> | <env-var>
kono
parents:
diff changeset
248 //
kono
parents:
diff changeset
249 // <env-var>:
kono
parents:
diff changeset
250 // variable=value
kono
parents:
diff changeset
251 // variable="value"
kono
parents:
diff changeset
252 // variable=
kono
parents:
diff changeset
253
kono
parents:
diff changeset
254 void MicEnvVar::mic_parse_env_var_list(
kono
parents:
diff changeset
255 int card_number, char *env_vars_def_list)
kono
parents:
diff changeset
256 {
kono
parents:
diff changeset
257 char *c = env_vars_def_list;
kono
parents:
diff changeset
258 char *env_var_name;
kono
parents:
diff changeset
259 int env_var_name_length;
kono
parents:
diff changeset
260 char *env_var_def;
kono
parents:
diff changeset
261 bool var_is_quoted;
kono
parents:
diff changeset
262
kono
parents:
diff changeset
263 if (*c == '"') {
kono
parents:
diff changeset
264 c++;
kono
parents:
diff changeset
265 }
kono
parents:
diff changeset
266 while (*c != 0) {
kono
parents:
diff changeset
267 var_is_quoted = false;
kono
parents:
diff changeset
268 env_var_name = c;
kono
parents:
diff changeset
269 env_var_name_length = 0;
kono
parents:
diff changeset
270 if (isalpha(*c)) {
kono
parents:
diff changeset
271 while (isalnum(*c) || *c == '_') {
kono
parents:
diff changeset
272 c++;
kono
parents:
diff changeset
273 env_var_name_length++;
kono
parents:
diff changeset
274 }
kono
parents:
diff changeset
275 }
kono
parents:
diff changeset
276 else {
kono
parents:
diff changeset
277 LIBOFFLOAD_ERROR(c_mic_parse_env_var_list1);
kono
parents:
diff changeset
278 return;
kono
parents:
diff changeset
279 }
kono
parents:
diff changeset
280 if (*c != '=') {
kono
parents:
diff changeset
281 LIBOFFLOAD_ERROR(c_mic_parse_env_var_list2);
kono
parents:
diff changeset
282 return;
kono
parents:
diff changeset
283 }
kono
parents:
diff changeset
284 c++;
kono
parents:
diff changeset
285
kono
parents:
diff changeset
286 if (*c == '"') {
kono
parents:
diff changeset
287 var_is_quoted = true;
kono
parents:
diff changeset
288 c++;
kono
parents:
diff changeset
289 }
kono
parents:
diff changeset
290 // Environment variable values that contain | will need to be escaped.
kono
parents:
diff changeset
291 while (*c != 0 && *c != '|' &&
kono
parents:
diff changeset
292 (!var_is_quoted || *c != '"'))
kono
parents:
diff changeset
293 {
kono
parents:
diff changeset
294 // skip escaped symbol
kono
parents:
diff changeset
295 if (*c == '\\') {
kono
parents:
diff changeset
296 c++;
kono
parents:
diff changeset
297 }
kono
parents:
diff changeset
298 c++;
kono
parents:
diff changeset
299 }
kono
parents:
diff changeset
300 if (var_is_quoted) {
kono
parents:
diff changeset
301 c++; // for "
kono
parents:
diff changeset
302 while (*c != 0 && *c != '|') {
kono
parents:
diff changeset
303 c++;
kono
parents:
diff changeset
304 }
kono
parents:
diff changeset
305 }
kono
parents:
diff changeset
306
kono
parents:
diff changeset
307 int sz = c - env_var_name;
kono
parents:
diff changeset
308 env_var_def = (char*)malloc(sz);
kono
parents:
diff changeset
309 if (env_var_def == NULL)
kono
parents:
diff changeset
310 LIBOFFLOAD_ERROR(c_malloc);
kono
parents:
diff changeset
311 memcpy(env_var_def, env_var_name, sz);
kono
parents:
diff changeset
312 env_var_def[sz] = 0;
kono
parents:
diff changeset
313
kono
parents:
diff changeset
314 if (*c == '|') {
kono
parents:
diff changeset
315 c++;
kono
parents:
diff changeset
316 while (*c != 0 && *c == ' ') {
kono
parents:
diff changeset
317 c++;
kono
parents:
diff changeset
318 }
kono
parents:
diff changeset
319 }
kono
parents:
diff changeset
320 add_env_var(card_number,
kono
parents:
diff changeset
321 env_var_name,
kono
parents:
diff changeset
322 env_var_name_length,
kono
parents:
diff changeset
323 env_var_def);
kono
parents:
diff changeset
324 }
kono
parents:
diff changeset
325 }
kono
parents:
diff changeset
326
kono
parents:
diff changeset
327 // Collect all definitions for the card with number "card_num".
kono
parents:
diff changeset
328 // The returned result is vector of string pointers defining one
kono
parents:
diff changeset
329 // environment variable. The vector is terminated by NULL pointer.
kono
parents:
diff changeset
330 // In the beginning of the vector there are env vars defined as
kono
parents:
diff changeset
331 // <mic-prefix>_<card-number>_<var>=<value>
kono
parents:
diff changeset
332 // or
kono
parents:
diff changeset
333 // <mic-prefix>_<card-number>_ENV=<env-vars>
kono
parents:
diff changeset
334 // where <card-number> is equal to "card_num"
kono
parents:
diff changeset
335 // They are followed by definitions valid for any card
kono
parents:
diff changeset
336 // and absent in previous definitions.
kono
parents:
diff changeset
337
kono
parents:
diff changeset
338 char** MicEnvVar::create_environ_for_card(int card_num)
kono
parents:
diff changeset
339 {
kono
parents:
diff changeset
340 VarValue *var_value;
kono
parents:
diff changeset
341 VarValue *var_value_find;
kono
parents:
diff changeset
342 CardEnvVars *card_data = get_card(card_num);
kono
parents:
diff changeset
343 CardEnvVars *card_data_common;
kono
parents:
diff changeset
344 std::list<char*> new_env;
kono
parents:
diff changeset
345 char **rez;
kono
parents:
diff changeset
346
kono
parents:
diff changeset
347 if (!prefix) {
kono
parents:
diff changeset
348 return NULL;
kono
parents:
diff changeset
349 }
kono
parents:
diff changeset
350 // There is no personel env var definitions for the card with
kono
parents:
diff changeset
351 // number "card_num"
kono
parents:
diff changeset
352 if (!card_data) {
kono
parents:
diff changeset
353 return create_environ_for_card(any_card);
kono
parents:
diff changeset
354 }
kono
parents:
diff changeset
355
kono
parents:
diff changeset
356 for (std::list<MicEnvVar::VarValue*>::const_iterator
kono
parents:
diff changeset
357 it = card_data->env_vars.begin();
kono
parents:
diff changeset
358 it != card_data->env_vars.end(); it++) {
kono
parents:
diff changeset
359 var_value = *it;
kono
parents:
diff changeset
360 new_env.push_back(var_value->env_var_value);
kono
parents:
diff changeset
361 }
kono
parents:
diff changeset
362
kono
parents:
diff changeset
363 if (card_num != any_card) {
kono
parents:
diff changeset
364 card_data_common = get_card(any_card);
kono
parents:
diff changeset
365 for (std::list<MicEnvVar::VarValue*>::const_iterator
kono
parents:
diff changeset
366 it = card_data_common->env_vars.begin();
kono
parents:
diff changeset
367 it != card_data_common->env_vars.end(); it++) {
kono
parents:
diff changeset
368 var_value = *it;
kono
parents:
diff changeset
369 var_value_find = card_data->find_var(var_value->env_var,
kono
parents:
diff changeset
370 var_value->length);
kono
parents:
diff changeset
371 if (!var_value_find) {
kono
parents:
diff changeset
372 new_env.push_back(var_value->env_var_value);
kono
parents:
diff changeset
373 }
kono
parents:
diff changeset
374 }
kono
parents:
diff changeset
375 }
kono
parents:
diff changeset
376
kono
parents:
diff changeset
377 int new_env_size = new_env.size();
kono
parents:
diff changeset
378 rez = (char**) malloc((new_env_size + 1) * sizeof(char*));
kono
parents:
diff changeset
379 if (rez == NULL)
kono
parents:
diff changeset
380 LIBOFFLOAD_ERROR(c_malloc);
kono
parents:
diff changeset
381 std::copy(new_env.begin(), new_env.end(), rez);
kono
parents:
diff changeset
382 rez[new_env_size] = 0;
kono
parents:
diff changeset
383 return rez;
kono
parents:
diff changeset
384 }