]> git.ipfire.org Git - thirdparty/bash.git/blob - arrayfunc.c
Imported from ../bash-3.2.48.tar.gz.
[thirdparty/bash.git] / arrayfunc.c
1 /* arrayfunc.c -- High-level array functions used by other parts of the shell. */
2
3 /* Copyright (C) 2001-2006 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with Bash; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
20
21 #include "config.h"
22
23 #if defined (ARRAY_VARS)
24
25 #if defined (HAVE_UNISTD_H)
26 # include <unistd.h>
27 #endif
28 #include <stdio.h>
29
30 #include "bashintl.h"
31
32 #include "shell.h"
33
34 #include "shmbutil.h"
35
36 #include "builtins/common.h"
37
38 extern char *this_command_name;
39 extern int last_command_exit_value;
40 extern int array_needs_making;
41
42 static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, int));
43
44 static void quote_array_assignment_chars __P((WORD_LIST *));
45 static char *array_value_internal __P((char *, int, int, int *));
46
47 /* Standard error message to use when encountering an invalid array subscript */
48 char *bash_badsub_errmsg = N_("bad array subscript");
49
50 /* **************************************************************** */
51 /* */
52 /* Functions to manipulate array variables and perform assignments */
53 /* */
54 /* **************************************************************** */
55
56 /* Convert a shell variable to an array variable. The original value is
57 saved as array[0]. */
58 SHELL_VAR *
59 convert_var_to_array (var)
60 SHELL_VAR *var;
61 {
62 char *oldval;
63 ARRAY *array;
64
65 oldval = value_cell (var);
66 array = array_create ();
67 if (oldval)
68 array_insert (array, 0, oldval);
69
70 FREE (value_cell (var));
71 var_setarray (var, array);
72
73 /* these aren't valid anymore */
74 var->dynamic_value = (sh_var_value_func_t *)NULL;
75 var->assign_func = (sh_var_assign_func_t *)NULL;
76
77 INVALIDATE_EXPORTSTR (var);
78 if (exported_p (var))
79 array_needs_making++;
80
81 VSETATTR (var, att_array);
82 VUNSETATTR (var, att_invisible);
83
84 return var;
85 }
86
87 static SHELL_VAR *
88 bind_array_var_internal (entry, ind, value, flags)
89 SHELL_VAR *entry;
90 arrayind_t ind;
91 char *value;
92 int flags;
93 {
94 SHELL_VAR *dentry;
95 char *newval;
96
97 /* If we're appending, we need the old value of the array reference, so
98 fake out make_variable_value with a dummy SHELL_VAR */
99 if (flags & ASS_APPEND)
100 {
101 dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
102 dentry->name = savestring (entry->name);
103 newval = array_reference (array_cell (entry), ind);
104 if (newval)
105 dentry->value = savestring (newval);
106 else
107 {
108 dentry->value = (char *)xmalloc (1);
109 dentry->value[0] = '\0';
110 }
111 dentry->exportstr = 0;
112 dentry->attributes = entry->attributes & ~(att_array|att_exported);
113 /* Leave the rest of the members uninitialized; the code doesn't look
114 at them. */
115 newval = make_variable_value (dentry, value, flags);
116 dispose_variable (dentry);
117 }
118 else
119 newval = make_variable_value (entry, value, flags);
120
121 if (entry->assign_func)
122 (*entry->assign_func) (entry, newval, ind);
123 else
124 array_insert (array_cell (entry), ind, newval);
125 FREE (newval);
126
127 return (entry);
128 }
129
130 /* Perform an array assignment name[ind]=value. If NAME already exists and
131 is not an array, and IND is 0, perform name=value instead. If NAME exists
132 and is not an array, and IND is not 0, convert it into an array with the
133 existing value as name[0].
134
135 If NAME does not exist, just create an array variable, no matter what
136 IND's value may be. */
137 SHELL_VAR *
138 bind_array_variable (name, ind, value, flags)
139 char *name;
140 arrayind_t ind;
141 char *value;
142 int flags;
143 {
144 SHELL_VAR *entry;
145
146 entry = var_lookup (name, shell_variables);
147
148 if (entry == (SHELL_VAR *) 0)
149 entry = make_new_array_variable (name);
150 else if (readonly_p (entry) || noassign_p (entry))
151 {
152 if (readonly_p (entry))
153 err_readonly (name);
154 return (entry);
155 }
156 else if (array_p (entry) == 0)
157 entry = convert_var_to_array (entry);
158
159 /* ENTRY is an array variable, and ARRAY points to the value. */
160 return (bind_array_var_internal (entry, ind, value, flags));
161 }
162
163 /* Parse NAME, a lhs of an assignment statement of the form v[s], and
164 assign VALUE to that array element by calling bind_array_variable(). */
165 SHELL_VAR *
166 assign_array_element (name, value, flags)
167 char *name, *value;
168 int flags;
169 {
170 char *sub, *vname;
171 arrayind_t ind;
172 int sublen;
173 SHELL_VAR *entry;
174
175 vname = array_variable_name (name, &sub, &sublen);
176
177 if (vname == 0)
178 return ((SHELL_VAR *)NULL);
179
180 if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
181 {
182 free (vname);
183 err_badarraysub (name);
184 return ((SHELL_VAR *)NULL);
185 }
186
187 ind = array_expand_index (sub, sublen);
188 if (ind < 0)
189 {
190 free (vname);
191 err_badarraysub (name);
192 return ((SHELL_VAR *)NULL);
193 }
194
195 entry = bind_array_variable (vname, ind, value, flags);
196
197 free (vname);
198 return (entry);
199 }
200
201 /* Find the array variable corresponding to NAME. If there is no variable,
202 create a new array variable. If the variable exists but is not an array,
203 convert it to an indexed array. If CHECK_FLAGS is non-zero, an existing
204 variable is checked for the readonly or noassign attribute in preparation
205 for assignment (e.g., by the `read' builtin). */
206 SHELL_VAR *
207 find_or_make_array_variable (name, check_flags)
208 char *name;
209 int check_flags;
210 {
211 SHELL_VAR *var;
212
213 var = find_variable (name);
214
215 if (var == 0)
216 var = make_new_array_variable (name);
217 else if (check_flags && (readonly_p (var) || noassign_p (var)))
218 {
219 if (readonly_p (var))
220 err_readonly (name);
221 return ((SHELL_VAR *)NULL);
222 }
223 else if (array_p (var) == 0)
224 var = convert_var_to_array (var);
225
226 return (var);
227 }
228
229 /* Perform a compound assignment statement for array NAME, where VALUE is
230 the text between the parens: NAME=( VALUE ) */
231 SHELL_VAR *
232 assign_array_from_string (name, value, flags)
233 char *name, *value;
234 int flags;
235 {
236 SHELL_VAR *var;
237
238 var = find_or_make_array_variable (name, 1);
239 if (var == 0)
240 return ((SHELL_VAR *)NULL);
241
242 return (assign_array_var_from_string (var, value, flags));
243 }
244
245 /* Sequentially assign the indices of indexed array variable VAR from the
246 words in LIST. */
247 SHELL_VAR *
248 assign_array_var_from_word_list (var, list, flags)
249 SHELL_VAR *var;
250 WORD_LIST *list;
251 int flags;
252 {
253 register arrayind_t i;
254 register WORD_LIST *l;
255 ARRAY *a;
256
257 a = array_cell (var);
258 i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
259
260 for (l = list; l; l = l->next, i++)
261 if (var->assign_func)
262 (*var->assign_func) (var, l->word->word, i);
263 else
264 array_insert (a, i, l->word->word);
265 return var;
266 }
267
268 WORD_LIST *
269 expand_compound_array_assignment (value, flags)
270 char *value;
271 int flags;
272 {
273 WORD_LIST *list, *nlist;
274 char *val;
275 int ni;
276
277 /* I don't believe this condition is ever true any more. */
278 if (*value == '(') /*)*/
279 {
280 ni = 1;
281 val = extract_array_assignment_list (value, &ni);
282 if (val == 0)
283 return (WORD_LIST *)NULL;
284 }
285 else
286 val = value;
287
288 /* Expand the value string into a list of words, performing all the
289 shell expansions including pathname generation and word splitting. */
290 /* First we split the string on whitespace, using the shell parser
291 (ksh93 seems to do this). */
292 list = parse_string_to_word_list (val, 1, "array assign");
293
294 /* If we're using [subscript]=value, we need to quote each [ and ] to
295 prevent unwanted filename expansion. */
296 if (list)
297 quote_array_assignment_chars (list);
298
299 /* Now that we've split it, perform the shell expansions on each
300 word in the list. */
301 nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
302
303 dispose_words (list);
304
305 if (val != value)
306 free (val);
307
308 return nlist;
309 }
310
311 void
312 assign_compound_array_list (var, nlist, flags)
313 SHELL_VAR *var;
314 WORD_LIST *nlist;
315 int flags;
316 {
317 ARRAY *a;
318 WORD_LIST *list;
319 char *w, *val, *nval;
320 int len, iflags;
321 arrayind_t ind, last_ind;
322
323 a = array_cell (var);
324
325 /* Now that we are ready to assign values to the array, kill the existing
326 value. */
327 if (a && (flags & ASS_APPEND) == 0)
328 array_flush (a);
329 last_ind = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
330
331 for (list = nlist; list; list = list->next)
332 {
333 iflags = flags;
334 w = list->word->word;
335
336 /* We have a word of the form [ind]=value */
337 if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
338 {
339 len = skipsubscript (w, 0);
340
341 /* XXX - changes for `+=' */
342 if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
343 {
344 nval = make_variable_value (var, w, flags);
345 if (var->assign_func)
346 (*var->assign_func) (var, nval, last_ind);
347 else
348 array_insert (a, last_ind, nval);
349 FREE (nval);
350 last_ind++;
351 continue;
352 }
353
354 if (len == 1)
355 {
356 err_badarraysub (w);
357 continue;
358 }
359
360 if (ALL_ELEMENT_SUB (w[1]) && len == 2)
361 {
362 report_error (_("%s: cannot assign to non-numeric index"), w);
363 continue;
364 }
365
366 ind = array_expand_index (w + 1, len);
367 if (ind < 0)
368 {
369 err_badarraysub (w);
370 continue;
371 }
372 last_ind = ind;
373 /* XXX - changes for `+=' -- just accept the syntax. ksh93 doesn't do this */
374 if (w[len + 1] == '+' && w[len + 2] == '=')
375 {
376 iflags |= ASS_APPEND;
377 val = w + len + 3;
378 }
379 else
380 val = w + len + 2;
381 }
382 else /* No [ind]=value, just a stray `=' */
383 {
384 ind = last_ind;
385 val = w;
386 }
387
388 if (integer_p (var))
389 this_command_name = (char *)NULL; /* no command name for errors */
390 bind_array_var_internal (var, ind, val, iflags);
391 last_ind++;
392 }
393 }
394
395 /* Perform a compound array assignment: VAR->name=( VALUE ). The
396 VALUE has already had the parentheses stripped. */
397 SHELL_VAR *
398 assign_array_var_from_string (var, value, flags)
399 SHELL_VAR *var;
400 char *value;
401 int flags;
402 {
403 WORD_LIST *nlist;
404
405 if (value == 0)
406 return var;
407
408 nlist = expand_compound_array_assignment (value, flags);
409 assign_compound_array_list (var, nlist, flags);
410
411 if (nlist)
412 dispose_words (nlist);
413 return (var);
414 }
415
416 /* For each word in a compound array assignment, if the word looks like
417 [ind]=value, quote the `[' and `]' before the `=' to protect them from
418 unwanted filename expansion. */
419 static void
420 quote_array_assignment_chars (list)
421 WORD_LIST *list;
422 {
423 char *s, *t, *nword;
424 int saw_eq;
425 WORD_LIST *l;
426
427 for (l = list; l; l = l->next)
428 {
429 if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
430 continue; /* should not happen, but just in case... */
431 /* Don't bother if it doesn't look like [ind]=value */
432 if (l->word->word[0] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */
433 continue;
434 s = nword = (char *)xmalloc (strlen (l->word->word) * 2 + 1);
435 saw_eq = 0;
436 for (t = l->word->word; *t; )
437 {
438 if (*t == '=')
439 saw_eq = 1;
440 if (saw_eq == 0 && (*t == '[' || *t == ']'))
441 *s++ = '\\';
442 *s++ = *t++;
443 }
444 *s = '\0';
445 free (l->word->word);
446 l->word->word = nword;
447 }
448 }
449
450 /* This function assumes s[i] == '['; returns with s[ret] == ']' if
451 an array subscript is correctly parsed. */
452 int
453 skipsubscript (s, i)
454 const char *s;
455 int i;
456 {
457 int count, c;
458 #if defined (HANDLE_MULTIBYTE)
459 mbstate_t state, state_bak;
460 size_t slength, mblength;
461 #endif
462
463 #if defined (HANDLE_MULTIBYTE)
464 memset (&state, '\0', sizeof (mbstate_t));
465 slength = strlen (s + i);
466 #endif
467
468 count = 1;
469 while (count)
470 {
471 /* Advance one (possibly multibyte) character in S starting at I. */
472 #if defined (HANDLE_MULTIBYTE)
473 if (MB_CUR_MAX > 1)
474 {
475 state_bak = state;
476 mblength = mbrlen (s + i, slength, &state);
477
478 if (MB_INVALIDCH (mblength))
479 {
480 state = state_bak;
481 i++;
482 slength--;
483 }
484 else if (MB_NULLWCH (mblength))
485 return i;
486 else
487 {
488 i += mblength;
489 slength -= mblength;
490 }
491 }
492 else
493 #endif
494 ++i;
495
496 c = s[i];
497
498 if (c == 0)
499 break;
500 else if (c == '[')
501 count++;
502 else if (c == ']')
503 count--;
504 }
505
506 return i;
507 }
508
509 /* This function is called with SUB pointing to just after the beginning
510 `[' of an array subscript and removes the array element to which SUB
511 expands from array VAR. A subscript of `*' or `@' unsets the array. */
512 int
513 unbind_array_element (var, sub)
514 SHELL_VAR *var;
515 char *sub;
516 {
517 int len;
518 arrayind_t ind;
519 ARRAY_ELEMENT *ae;
520
521 len = skipsubscript (sub, 0);
522 if (sub[len] != ']' || len == 0)
523 {
524 builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
525 return -1;
526 }
527 sub[len] = '\0';
528
529 if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
530 {
531 unbind_variable (var->name);
532 return (0);
533 }
534 ind = array_expand_index (sub, len+1);
535 if (ind < 0)
536 {
537 builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
538 return -1;
539 }
540 ae = array_remove (array_cell (var), ind);
541 if (ae)
542 array_dispose_element (ae);
543 return 0;
544 }
545
546 /* Format and output an array assignment in compound form VAR=(VALUES),
547 suitable for re-use as input. */
548 void
549 print_array_assignment (var, quoted)
550 SHELL_VAR *var;
551 int quoted;
552 {
553 char *vstr;
554
555 vstr = array_to_assign (array_cell (var), quoted);
556
557 if (vstr == 0)
558 printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
559 else
560 {
561 printf ("%s=%s\n", var->name, vstr);
562 free (vstr);
563 }
564 }
565
566 /***********************************************************************/
567 /* */
568 /* Utility functions to manage arrays and their contents for expansion */
569 /* */
570 /***********************************************************************/
571
572 /* Return 1 if NAME is a properly-formed array reference v[sub]. */
573 int
574 valid_array_reference (name)
575 char *name;
576 {
577 char *t;
578 int r, len;
579
580 t = xstrchr (name, '['); /* ] */
581 if (t)
582 {
583 *t = '\0';
584 r = legal_identifier (name);
585 *t = '[';
586 if (r == 0)
587 return 0;
588 /* Check for a properly-terminated non-blank subscript. */
589 len = skipsubscript (t, 0);
590 if (t[len] != ']' || len == 1)
591 return 0;
592 for (r = 1; r < len; r++)
593 if (whitespace (t[r]) == 0)
594 return 1;
595 return 0;
596 }
597 return 0;
598 }
599
600 /* Expand the array index beginning at S and extending LEN characters. */
601 arrayind_t
602 array_expand_index (s, len)
603 char *s;
604 int len;
605 {
606 char *exp, *t;
607 int expok;
608 arrayind_t val;
609
610 exp = (char *)xmalloc (len);
611 strncpy (exp, s, len - 1);
612 exp[len - 1] = '\0';
613 t = expand_arith_string (exp, 0);
614 this_command_name = (char *)NULL;
615 val = evalexp (t, &expok);
616 free (t);
617 free (exp);
618 if (expok == 0)
619 {
620 last_command_exit_value = EXECUTION_FAILURE;
621
622 top_level_cleanup ();
623 jump_to_top_level (DISCARD);
624 }
625 return val;
626 }
627
628 /* Return the name of the variable specified by S without any subscript.
629 If SUBP is non-null, return a pointer to the start of the subscript
630 in *SUBP. If LENP is non-null, the length of the subscript is returned
631 in *LENP. This returns newly-allocated memory. */
632 char *
633 array_variable_name (s, subp, lenp)
634 char *s, **subp;
635 int *lenp;
636 {
637 char *t, *ret;
638 int ind, ni;
639
640 t = xstrchr (s, '[');
641 if (t == 0)
642 {
643 if (subp)
644 *subp = t;
645 if (lenp)
646 *lenp = 0;
647 return ((char *)NULL);
648 }
649 ind = t - s;
650 ni = skipsubscript (s, ind);
651 if (ni <= ind + 1 || s[ni] != ']')
652 {
653 err_badarraysub (s);
654 if (subp)
655 *subp = t;
656 if (lenp)
657 *lenp = 0;
658 return ((char *)NULL);
659 }
660
661 *t = '\0';
662 ret = savestring (s);
663 *t++ = '['; /* ] */
664
665 if (subp)
666 *subp = t;
667 if (lenp)
668 *lenp = ni - ind;
669
670 return ret;
671 }
672
673 /* Return the variable specified by S without any subscript. If SUBP is
674 non-null, return a pointer to the start of the subscript in *SUBP.
675 If LENP is non-null, the length of the subscript is returned in *LENP. */
676 SHELL_VAR *
677 array_variable_part (s, subp, lenp)
678 char *s, **subp;
679 int *lenp;
680 {
681 char *t;
682 SHELL_VAR *var;
683
684 t = array_variable_name (s, subp, lenp);
685 if (t == 0)
686 return ((SHELL_VAR *)NULL);
687 var = find_variable (t);
688
689 free (t);
690 return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
691 }
692
693 /* Return a string containing the elements in the array and subscript
694 described by S. If the subscript is * or @, obeys quoting rules akin
695 to the expansion of $* and $@ including double quoting. If RTYPE
696 is non-null it gets 1 if the array reference is name[@] or name[*]
697 and 0 otherwise. */
698 static char *
699 array_value_internal (s, quoted, allow_all, rtype)
700 char *s;
701 int quoted, allow_all, *rtype;
702 {
703 int len;
704 arrayind_t ind;
705 char *retval, *t, *temp;
706 WORD_LIST *l;
707 SHELL_VAR *var;
708
709 var = array_variable_part (s, &t, &len);
710
711 /* Expand the index, even if the variable doesn't exist, in case side
712 effects are needed, like ${w[i++]} where w is unset. */
713 #if 0
714 if (var == 0)
715 return (char *)NULL;
716 #endif
717
718 if (len == 0)
719 return ((char *)NULL); /* error message already printed */
720
721 /* [ */
722 if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
723 {
724 if (rtype)
725 *rtype = (t[0] == '*') ? 1 : 2;
726 if (allow_all == 0)
727 {
728 err_badarraysub (s);
729 return ((char *)NULL);
730 }
731 else if (var == 0 || value_cell (var) == 0)
732 return ((char *)NULL);
733 else if (array_p (var) == 0)
734 l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
735 else
736 {
737 l = array_to_word_list (array_cell (var));
738 if (l == (WORD_LIST *)NULL)
739 return ((char *) NULL);
740 }
741
742 if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
743 {
744 temp = string_list_dollar_star (l);
745 retval = quote_string (temp);
746 free (temp);
747 }
748 else /* ${name[@]} or unquoted ${name[*]} */
749 retval = string_list_dollar_at (l, quoted);
750
751 dispose_words (l);
752 }
753 else
754 {
755 if (rtype)
756 *rtype = 0;
757 ind = array_expand_index (t, len);
758 if (ind < 0)
759 {
760 if (var)
761 err_badarraysub (var->name);
762 else
763 {
764 t[-1] = '\0';
765 err_badarraysub (s);
766 t[-1] = '['; /* ] */
767 }
768 return ((char *)NULL);
769 }
770 if (var == 0)
771 return ((char *)NULL);
772 if (array_p (var) == 0)
773 return (ind == 0 ? value_cell (var) : (char *)NULL);
774 retval = array_reference (array_cell (var), ind);
775 }
776
777 return retval;
778 }
779
780 /* Return a string containing the elements described by the array and
781 subscript contained in S, obeying quoting for subscripts * and @. */
782 char *
783 array_value (s, quoted, rtype)
784 char *s;
785 int quoted, *rtype;
786 {
787 return (array_value_internal (s, quoted, 1, rtype));
788 }
789
790 /* Return the value of the array indexing expression S as a single string.
791 If ALLOW_ALL is 0, do not allow `@' and `*' subscripts. This is used
792 by other parts of the shell such as the arithmetic expression evaluator
793 in expr.c. */
794 char *
795 get_array_value (s, allow_all, rtype)
796 char *s;
797 int allow_all, *rtype;
798 {
799 return (array_value_internal (s, 0, allow_all, rtype));
800 }
801
802 char *
803 array_keys (s, quoted)
804 char *s;
805 int quoted;
806 {
807 int len;
808 char *retval, *t, *temp;
809 WORD_LIST *l;
810 SHELL_VAR *var;
811
812 var = array_variable_part (s, &t, &len);
813
814 /* [ */
815 if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
816 return (char *)NULL;
817
818 if (array_p (var) == 0)
819 l = add_string_to_list ("0", (WORD_LIST *)NULL);
820 else
821 {
822 l = array_keys_to_word_list (array_cell (var));
823 if (l == (WORD_LIST *)NULL)
824 return ((char *) NULL);
825 }
826
827 if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
828 {
829 temp = string_list_dollar_star (l);
830 retval = quote_string (temp);
831 free (temp);
832 }
833 else /* ${!name[@]} or unquoted ${!name[*]} */
834 retval = string_list_dollar_at (l, quoted);
835
836 dispose_words (l);
837 return retval;
838 }
839 #endif /* ARRAY_VARS */