GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
variables.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 1993-2013 John W. Eaton
4 Copyright (C) 2009-2010 VZLU Prague
5 
6 This file is part of Octave.
7 
8 Octave is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 Octave is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, see
20 <http://www.gnu.org/licenses/>.
21 
22 */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <cstdio>
29 #include <cstring>
30 
31 #include <iomanip>
32 #include <set>
33 #include <string>
34 
35 #include "file-stat.h"
36 #include "oct-env.h"
37 #include "file-ops.h"
38 #include "glob-match.h"
39 #include "lo-regexp.h"
40 #include "str-vec.h"
41 
42 #include <defaults.h>
43 #include "Cell.h"
44 #include "defun.h"
45 #include "dirfns.h"
46 #include "error.h"
47 #include "gripes.h"
48 #include "help.h"
49 #include "input.h"
50 #include "lex.h"
51 #include "load-path.h"
52 #include "octave-link.h"
53 #include "oct-map.h"
54 #include "oct-obj.h"
55 #include "ov.h"
56 #include "ov-class.h"
57 #include "ov-usr-fcn.h"
58 #include "pager.h"
59 #include "parse.h"
60 #include "symtab.h"
61 #include "toplev.h"
62 #include "unwind-prot.h"
63 #include "utils.h"
64 #include "variables.h"
65 
66 // Defines layout for the whos/who -long command
67 static std::string Vwhos_line_format
68  = " %a:4; %ln:6; %cs:16:6:1; %rb:12; %lc:-1;\n";
69 
70 void
72 {
74 }
75 
76 void
77 clear_function (const std::string& nm)
78 {
80 }
81 
82 void
83 clear_variable (const std::string& nm)
84 {
86 }
87 
88 void
89 clear_symbol (const std::string& nm)
90 {
92 }
93 
94 // Attributes of variables and functions.
95 
96 // Is this octave_value a valid function?
97 
99 is_valid_function (const std::string& fcn_name,
100  const std::string& warn_for, bool warn)
101 {
102  octave_function *ans = 0;
103 
104  if (! fcn_name.empty ())
105  {
106  octave_value val = symbol_table::find_function (fcn_name);
107 
108  if (val.is_defined ())
109  ans = val.function_value (true);
110  }
111 
112  if (! ans && warn)
113  error ("%s: the symbol '%s' is not valid as a function",
114  warn_for.c_str (), fcn_name.c_str ());
115 
116  return ans;
117 }
118 
121  const std::string& warn_for, bool warn)
122 {
123  octave_function *ans = 0;
124 
125  std::string fcn_name;
126 
127  if (arg.is_string ())
128  {
129  fcn_name = arg.string_value ();
130 
131  if (! error_state)
132  ans = is_valid_function (fcn_name, warn_for, warn);
133  else if (warn)
134  error ("%s: expecting function name as argument", warn_for.c_str ());
135  }
136  else if (warn)
137  error ("%s: expecting function name as argument", warn_for.c_str ());
138 
139  return ans;
140 }
141 
143 extract_function (const octave_value& arg, const std::string& warn_for,
144  const std::string& fname, const std::string& header,
145  const std::string& trailer)
146 {
147  octave_function *retval = 0;
148 
149  retval = is_valid_function (arg, warn_for, 0);
150 
151  if (! retval)
152  {
153  std::string s = arg.string_value ();
154 
155  std::string cmd = header;
156  cmd.append (s);
157  cmd.append (trailer);
158 
159  if (! error_state)
160  {
161  int parse_status;
162 
163  eval_string (cmd, true, parse_status, 0);
164 
165  if (parse_status == 0)
166  {
167  retval = is_valid_function (fname, warn_for, 0);
168 
169  if (! retval)
170  {
171  error ("%s: '%s' is not valid as a function",
172  warn_for.c_str (), fname.c_str ());
173  return retval;
174  }
175 
176  warning ("%s: passing function body as a string is obsolete; please use anonymous functions",
177  warn_for.c_str ());
178  }
179  else
180  error ("%s: '%s' is not valid as a function",
181  warn_for.c_str (), fname.c_str ());
182  }
183  else
184  error ("%s: expecting first argument to be a string",
185  warn_for.c_str ());
186  }
187 
188  return retval;
189 }
190 
192 get_struct_elts (const std::string& text)
193 {
194  int n = 1;
195 
196  size_t pos = 0;
197 
198  size_t len = text.length ();
199 
200  while ((pos = text.find ('.', pos)) != std::string::npos)
201  {
202  if (++pos == len)
203  break;
204 
205  n++;
206  }
207 
208  string_vector retval (n);
209 
210  pos = 0;
211 
212  for (int i = 0; i < n; i++)
213  {
214  len = text.find ('.', pos);
215 
216  if (len != std::string::npos)
217  len -= pos;
218 
219  retval[i] = text.substr (pos, len);
220 
221  if (len != std::string::npos)
222  pos += len + 1;
223  }
224 
225  return retval;
226 }
227 
228 static inline bool
229 is_variable (const std::string& name)
230 {
231  bool retval = false;
232 
233  if (! name.empty ())
234  {
235  octave_value val = symbol_table::varval (name);
236 
237  retval = val.is_defined ();
238  }
239 
240  return retval;
241 }
242 
244 generate_struct_completions (const std::string& text,
245  std::string& prefix, std::string& hint)
246 {
248 
249  size_t pos = text.rfind ('.');
250 
251  if (pos != std::string::npos)
252  {
253  if (pos == text.length ())
254  hint = "";
255  else
256  hint = text.substr (pos+1);
257 
258  prefix = text.substr (0, pos);
259 
260  std::string base_name = prefix;
261 
262  pos = base_name.find_first_of ("{(.");
263 
264  if (pos != std::string::npos)
265  base_name = base_name.substr (0, pos);
266 
267  if (is_variable (base_name))
268  {
269  int parse_status;
270 
271  unwind_protect frame;
272 
273  frame.protect_var (error_state);
274  frame.protect_var (warning_state);
275 
278 
279  discard_error_messages = true;
281 
282  octave_value tmp = eval_string (prefix, true, parse_status);
283 
284  frame.run ();
285 
286  if (tmp.is_defined () && (tmp.is_map () || tmp.is_java ()))
287  names = tmp.map_keys ();
288  }
289  }
290 
291  return names;
292 }
293 
294 // FIXME: this will have to be much smarter to work "correctly".
295 
296 bool
297 looks_like_struct (const std::string& text)
298 {
299  bool retval = (! text.empty ()
300  && text != "."
301  && text.find_first_of (file_ops::dir_sep_chars ()) == std::string::npos
302  && text.find ("..") == std::string::npos
303  && text.rfind ('.') != std::string::npos);
304 
305 #if 0
306  symbol_record *sr = curr_sym_tab->lookup (text);
307 
308  if (sr && ! sr->is_function ())
309  {
310  int parse_status;
311 
312  unwind_protect frame;
313 
315  frame.protect_var (error_state);
316 
317  discard_error_messages = true;
318 
319  octave_value tmp = eval_string (text, true, parse_status);
320 
321  frame.run ();
322 
323  retval = (tmp.is_defined () && tmp.is_map ());
324  }
325 #endif
326 
327  return retval;
328 }
329 
330 static octave_value
332 {
333  octave_value retval = false;
334 
335  int nargin = args.length ();
336 
337  if (nargin != 1)
338  {
339  print_usage ();
340  return retval;
341  }
342 
343  std::string name = args(0).string_value ();
344 
345  if (error_state)
346  {
347  error ("isglobal: NAME must be a string");
348  return retval;
349  }
350 
351  return symbol_table::is_global (name);
352 }
353 
354 DEFUN (isglobal, args, ,
355  "-*- texinfo -*-\n\
356 @deftypefn {Built-in Function} {} isglobal (@var{name})\n\
357 Return true if @var{name} is a globally visible variable.\n\
358 For example:\n\
359 \n\
360 @example\n\
361 @group\n\
362 global x\n\
363 isglobal (\"x\")\n\
364  @result{} 1\n\
365 @end group\n\
366 @end example\n\
367 @seealso{isvarname, exist}\n\
368 @end deftypefn")
369 {
370  return do_isglobal (args);
371 }
372 
373 static octave_value
374 safe_symbol_lookup (const std::string& symbol_name)
375 {
376  octave_value retval;
377 
378  unwind_protect frame;
379  interpreter_try (frame);
380 
381  retval = symbol_table::find (symbol_name);
382 
383  error_state = 0;
384 
385  return retval;
386 }
387 
388 int
389 symbol_exist (const std::string& name, const std::string& type)
390 {
391  std::string struct_elts;
392  std::string symbol_name = name;
393 
394  size_t pos = name.find ('.');
395 
396  if (pos != std::string::npos && pos > 0)
397  {
398  struct_elts = name.substr (pos+1);
399  symbol_name = name.substr (0, pos);
400  }
401  else if (is_keyword (symbol_name))
402  return 0;
403 
404  bool search_any = type == "any";
405  bool search_var = type == "var";
406  bool search_dir = type == "dir";
407  bool search_file = type == "file";
408  bool search_builtin = type == "builtin";
409 
410  if (search_any || search_var)
411  {
412  bool not_a_struct = struct_elts.empty ();
413  bool var_ok = not_a_struct; // || val.is_map_element (struct_elts)
414 
415  octave_value val = symbol_table::varval (name);
416 
417  if (var_ok && (val.is_constant () || val.is_object ()
418  || val.is_function_handle ()
419  || val.is_anonymous_function ()
420  || val.is_inline_function ()))
421  return 1;
422 
423  if (search_var)
424  return 0;
425  }
426 
427  if (search_any || search_file || search_dir)
428  {
429  std::string file_name = lookup_autoload (name);
430 
431  if (file_name.empty ())
432  file_name = load_path::find_fcn (name);
433 
434  size_t len = file_name.length ();
435 
436  if (len > 0)
437  {
438  if (search_any || search_file)
439  {
440  if (len > 4 && (file_name.substr (len-4) == ".oct"
441  || file_name.substr (len-4) == ".mex"))
442  return 3;
443  else
444  return 2;
445  }
446  }
447 
448  file_name = file_in_path (name, "");
449 
450  if (file_name.empty ())
451  file_name = name;
452 
453  file_stat fs (file_name);
454 
455  if (fs)
456  {
457  if (search_any || search_file)
458  return fs.is_dir () ? 7 : 2;
459  else if (search_dir && fs.is_dir ())
460  return 7;
461  }
462 
463  if (search_file || search_dir)
464  return 0;
465  }
466 
467  // We shouldn't need to look in the global symbol table, since any
468  // name that is visible in the current scope will be in the local
469  // symbol table.
470 
471  octave_value val = safe_symbol_lookup (symbol_name);
472 
473  if (val.is_defined () && struct_elts.empty ())
474  {
475  if ((search_any || search_builtin)
476  && val.is_builtin_function ())
477  return 5;
478 
479  if ((search_any || search_file)
480  && (val.is_user_function () || val.is_dld_function ()))
481  {
482  octave_function *f = val.function_value (true);
483  std::string s = f ? f->fcn_file_name () : std::string ();
484 
485  return s.empty () ? 103 : (val.is_user_function () ? 2 : 3);
486  }
487  }
488 
489  return 0;
490 }
491 
492 #define GET_IDX(LEN) \
493  static_cast<int> ((LEN-1) * static_cast<double> (rand ()) / RAND_MAX)
494 
495 std::string
496 unique_symbol_name (const std::string& basename)
497 {
498  static const std::string alpha
499  = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
500 
501  static size_t len = alpha.length ();
502 
503  std::string nm = basename + alpha[GET_IDX (len)];
504 
505  size_t pos = nm.length ();
506 
507  if (nm.substr (0, 2) == "__")
508  nm.append ("__");
509 
510  while (symbol_exist (nm, "any"))
511  nm.insert (pos++, 1, alpha[GET_IDX (len)]);
512 
513  return nm;
514 }
515 
516 DEFUN (exist, args, ,
517  "-*- texinfo -*-\n\
518 @deftypefn {Built-in Function} {} exist (@var{name}, @var{type})\n\
519 Return 1 if the name exists as a variable, 2 if the name is an\n\
520 absolute file name, an ordinary file in Octave's @code{path}, or (after\n\
521 appending @samp{.m}) a function file in Octave's @code{path}, 3 if the\n\
522 name is a @samp{.oct} or @samp{.mex} file in Octave's @code{path},\n\
523 5 if the name is a built-in function, 7 if the name is a directory, or 103\n\
524 if the name is a function not associated with a file (entered on\n\
525 the command line).\n\
526 \n\
527 Otherwise, return 0.\n\
528 \n\
529 This function also returns 2 if a regular file called @var{name}\n\
530 exists in Octave's search path. If you want information about\n\
531 other types of files, you should use some combination of the functions\n\
532 @code{file_in_path} and @code{stat} instead.\n\
533 \n\
534 If the optional argument @var{type} is supplied, check only for\n\
535 symbols of the specified type. Valid types are\n\
536 \n\
537 @table @asis\n\
538 @item @qcode{\"var\"}\n\
539 Check only for variables.\n\
540 \n\
541 @item @qcode{\"builtin\"}\n\
542 Check only for built-in functions.\n\
543 \n\
544 @item @qcode{\"file\"}\n\
545 Check only for files and directories.\n\
546 \n\
547 @item @qcode{\"dir\"}\n\
548 Check only for directories.\n\
549 @end table\n\
550 \n\
551 @seealso{file_in_loadpath, file_in_path, find_dir_in_path, stat}\n\
552 @end deftypefn")
553 {
554  octave_value retval = false;
555 
556  int nargin = args.length ();
557 
558  if (nargin == 1 || nargin == 2)
559  {
560  std::string name = args(0).string_value ();
561 
562  if (! error_state)
563  {
564  if (nargin == 2)
565  {
566  std::string type = args(1).string_value ();
567 
568  if (! error_state)
569  retval = symbol_exist (name, type);
570  else
571  error ("exist: TYPE must be a string");
572  }
573  else
574  retval = symbol_exist (name);
575  }
576  else
577  error ("exist: NAME must be a string");
578  }
579  else
580  print_usage ();
581 
582  return retval;
583 }
584 
585 /*
586 %!test
587 %! if (isunix ())
588 %! assert (exist ("/tmp") == 7);
589 %! assert (exist ("/tmp", "file") == 7);
590 %! assert (exist ("/tmp", "dir") == 7);
591 %! assert (exist ("/bin/sh") == 2);
592 %! assert (exist ("/bin/sh", "file") == 2);
593 %! assert (exist ("/bin/sh", "dir") == 0);
594 %! assert (exist ("/dev/null") == 2);
595 %! assert (exist ("/dev/null", "file") == 2);
596 %! assert (exist ("/dev/null", "dir") == 0);
597 %! endif
598 */
599 
601 lookup_function_handle (const std::string& nm)
602 {
604 
605  return val.is_function_handle () ? val : octave_value ();
606 }
607 
609 get_global_value (const std::string& nm, bool silent)
610 {
612 
613  if (val.is_undefined () && ! silent)
614  error ("get_global_value: undefined symbol '%s'", nm.c_str ());
615 
616  return val;
617 }
618 
619 void
620 set_global_value (const std::string& nm, const octave_value& val)
621 {
622  symbol_table::global_assign (nm, val);
623 }
624 
626 get_top_level_value (const std::string& nm, bool silent)
627 {
629 
630  if (val.is_undefined () && ! silent)
631  error ("get_top_level_value: undefined symbol '%s'", nm.c_str ());
632 
633  return val;
634 }
635 
636 void
637 set_top_level_value (const std::string& nm, const octave_value& val)
638 {
640 }
641 
642 // Variable values.
643 
644 static bool
645 wants_local_change (const octave_value_list& args, int& nargin)
646 {
647  bool retval = false;
648 
649  if (nargin == 2)
650  {
651  if (args(1).is_string () && args(1).string_value () == "local")
652  {
653  nargin = 1;
654  retval = true;
655  }
656  else
657  {
658  error_with_cfn ("expecting second argument to be \"local\"");
659  nargin = 0;
660  }
661  }
662 
663  return retval;
664 }
665 
666 template <class T>
667 bool try_local_protect (T& var)
668 {
670  octave_user_function *curr_usr_fcn = 0;
671  if (curr_usr_code && curr_usr_code->is_user_function ())
672  curr_usr_fcn = dynamic_cast<octave_user_function *> (curr_usr_code);
673 
674  if (curr_usr_fcn && curr_usr_fcn->local_protect (var))
675  return true;
676  else
677  return false;
678 }
679 
681 set_internal_variable (bool& var, const octave_value_list& args,
682  int nargout, const char *nm)
683 {
684  octave_value retval;
685 
686  int nargin = args.length ();
687 
688  if (nargout > 0 || nargin == 0)
689  retval = var;
690 
691  if (wants_local_change (args, nargin))
692  {
693  if (! try_local_protect (var))
694  warning ("\"local\" has no effect outside a function");
695  }
696 
697  if (nargin == 1)
698  {
699  bool bval = args(0).bool_value ();
700 
701  if (! error_state)
702  var = bval;
703  else
704  error ("%s: expecting arg to be a logical value", nm);
705  }
706  else if (nargin > 1)
707  print_usage ();
708 
709  return retval;
710 }
711 
713 set_internal_variable (char& var, const octave_value_list& args,
714  int nargout, const char *nm)
715 {
716  octave_value retval;
717 
718  int nargin = args.length ();
719 
720  if (nargout > 0 || nargin == 0)
721  retval = var;
722 
723  if (wants_local_change (args, nargin))
724  {
725  if (! try_local_protect (var))
726  warning ("\"local\" has no effect outside a function");
727  }
728 
729  if (nargin == 1)
730  {
731  std::string sval = args(0).string_value ();
732 
733  if (! error_state)
734  {
735  switch (sval.length ())
736  {
737  case 1:
738  var = sval[0];
739  break;
740 
741  case 0:
742  var = '\0';
743  break;
744 
745  default:
746  error ("%s: argument must be a single character", nm);
747  break;
748  }
749  }
750  else
751  error ("%s: argument must be a single character", nm);
752  }
753  else if (nargin > 1)
754  print_usage ();
755 
756  return retval;
757 }
758 
761  int nargout, const char *nm,
762  int minval, int maxval)
763 {
764  octave_value retval;
765 
766  int nargin = args.length ();
767 
768  if (nargout > 0 || nargin == 0)
769  retval = var;
770 
771  if (wants_local_change (args, nargin))
772  {
773  if (! try_local_protect (var))
774  warning ("\"local\" has no effect outside a function");
775  }
776 
777  if (nargin == 1)
778  {
779  int ival = args(0).int_value ();
780 
781  if (! error_state)
782  {
783  if (ival < minval)
784  error ("%s: expecting arg to be greater than %d", nm, minval);
785  else if (ival > maxval)
786  error ("%s: expecting arg to be less than or equal to %d",
787  nm, maxval);
788  else
789  var = ival;
790  }
791  else
792  error ("%s: expecting arg to be an integer value", nm);
793  }
794  else if (nargin > 1)
795  print_usage ();
796 
797  return retval;
798 }
799 
801 set_internal_variable (double& var, const octave_value_list& args,
802  int nargout, const char *nm,
803  double minval, double maxval)
804 {
805  octave_value retval;
806 
807  int nargin = args.length ();
808 
809  if (nargout > 0 || nargin == 0)
810  retval = var;
811 
812  if (wants_local_change (args, nargin))
813  {
814  if (! try_local_protect (var))
815  warning ("\"local\" has no effect outside a function");
816  }
817 
818  if (nargin == 1)
819  {
820  double dval = args(0).scalar_value ();
821 
822  if (! error_state)
823  {
824  if (dval < minval)
825  error ("%s: expecting arg to be greater than %g", minval);
826  else if (dval > maxval)
827  error ("%s: expecting arg to be less than or equal to %g", maxval);
828  else
829  var = dval;
830  }
831  else
832  error ("%s: expecting arg to be a scalar value", nm);
833  }
834  else if (nargin > 1)
835  print_usage ();
836 
837  return retval;
838 }
839 
841 set_internal_variable (std::string& var, const octave_value_list& args,
842  int nargout, const char *nm, bool empty_ok)
843 {
844  octave_value retval;
845 
846  int nargin = args.length ();
847 
848  if (nargout > 0 || nargin == 0)
849  retval = var;
850 
851  if (wants_local_change (args, nargin))
852  {
853  if (! try_local_protect (var))
854  warning ("\"local\" has no effect outside a function");
855  }
856 
857  if (nargin == 1)
858  {
859  std::string sval = args(0).string_value ();
860 
861  if (! error_state)
862  {
863  if (empty_ok || ! sval.empty ())
864  var = sval;
865  else
866  error ("%s: value must not be empty", nm);
867  }
868  else
869  error ("%s: expecting arg to be a character string", nm);
870  }
871  else if (nargin > 1)
872  print_usage ();
873 
874  return retval;
875 }
876 
879  int nargout, const char *nm, const char **choices)
880 {
881  octave_value retval;
882  int nchoices = 0;
883  while (choices[nchoices] != 0)
884  nchoices++;
885 
886  int nargin = args.length ();
887  assert (var < nchoices);
888 
889  if (nargout > 0 || nargin == 0)
890  retval = choices[var];
891 
892  if (wants_local_change (args, nargin))
893  {
894  if (! try_local_protect (var))
895  warning ("\"local\" has no effect outside a function");
896  }
897 
898  if (nargin == 1)
899  {
900  std::string sval = args(0).string_value ();
901 
902  if (! error_state)
903  {
904  int i = 0;
905  for (; i < nchoices; i++)
906  {
907  if (sval == choices[i])
908  {
909  var = i;
910  break;
911  }
912  }
913  if (i == nchoices)
914  error ("%s: value not allowed (\"%s\")", nm, sval.c_str ());
915  }
916  else
917  error ("%s: expecting arg to be a character string", nm);
918  }
919  else if (nargin > 1)
920  print_usage ();
921 
922  return retval;
923 }
924 
925 struct
927 {
928  char command;
929  char modifier;
932  int balance;
933  std::string text;
934  std::string line;
935 };
936 
937 static void
938 print_descriptor (std::ostream& os, std::list<whos_parameter> params)
939 {
940  // This method prints a line of information on a given symbol
941  std::list<whos_parameter>::iterator i = params.begin ();
942  std::ostringstream param_buf;
943 
944  octave_preserve_stream_state stream_state (os);
945 
946  while (i != params.end ())
947  {
948  whos_parameter param = *i;
949 
950  if (param.command != '\0')
951  {
952  // Do the actual printing
953  switch (param.modifier)
954  {
955  case 'l':
956  os << std::setiosflags (std::ios::left)
957  << std::setw (param.parameter_length);
958  param_buf << std::setiosflags (std::ios::left)
959  << std::setw (param.parameter_length);
960  break;
961 
962  case 'r':
963  os << std::setiosflags (std::ios::right)
964  << std::setw (param.parameter_length);
965  param_buf << std::setiosflags (std::ios::right)
966  << std::setw (param.parameter_length);
967  break;
968 
969  case 'c':
970  if (param.command != 's')
971  {
972  os << std::setiosflags (std::ios::left)
973  << std::setw (param.parameter_length);
974  param_buf << std::setiosflags (std::ios::left)
975  << std::setw (param.parameter_length);
976  }
977  break;
978 
979  default:
980  os << std::setiosflags (std::ios::left)
981  << std::setw (param.parameter_length);
982  param_buf << std::setiosflags (std::ios::left)
983  << std::setw (param.parameter_length);
984  }
985 
986  if (param.command == 's' && param.modifier == 'c')
987  {
988  int a, b;
989 
990  if (param.modifier == 'c')
991  {
992  a = param.first_parameter_length - param.balance;
993  a = (a < 0 ? 0 : a);
994  b = param.parameter_length - a - param.text . length ();
995  b = (b < 0 ? 0 : b);
996  os << std::setiosflags (std::ios::left) << std::setw (a)
997  << "" << std::resetiosflags (std::ios::left) << param.text
998  << std::setiosflags (std::ios::left)
999  << std::setw (b) << ""
1000  << std::resetiosflags (std::ios::left);
1001  param_buf << std::setiosflags (std::ios::left)
1002  << std::setw (a)
1003  << "" << std::resetiosflags (std::ios::left)
1004  << param.line
1005  << std::setiosflags (std::ios::left)
1006  << std::setw (b) << ""
1007  << std::resetiosflags (std::ios::left);
1008  }
1009  }
1010  else
1011  {
1012  os << param.text;
1013  param_buf << param.line;
1014  }
1015  os << std::resetiosflags (std::ios::left)
1016  << std::resetiosflags (std::ios::right);
1017  param_buf << std::resetiosflags (std::ios::left)
1018  << std::resetiosflags (std::ios::right);
1019  i++;
1020  }
1021  else
1022  {
1023  os << param.text;
1024  param_buf << param.line;
1025  i++;
1026  }
1027  }
1028 
1029  os << param_buf.str ();
1030 }
1031 
1032 // FIXME: This is a bit of a kluge. We'd like to just use val.dims()
1033 // and if val is an object, expect that dims will call size if it is
1034 // overloaded by a user-defined method. But there are currently some
1035 // unresolved const issues that prevent that solution from working.
1036 
1037 std::string
1039 {
1040  octave_value tmp = val;
1041 
1042  Matrix sz = tmp.size ();
1043 
1044  dim_vector dv = dim_vector::alloc (sz.numel ());
1045 
1046  for (octave_idx_type i = 0; i < dv.length (); i++)
1047  dv(i) = sz(i);
1048 
1049  return dv.str ();
1050 }
1051 
1052 class
1054 {
1055 private:
1057  {
1059  const std::string& expr_str = std::string (),
1060  const octave_value& expr_val = octave_value ())
1061  : name (expr_str.empty () ? sr.name () : expr_str),
1062  varval (expr_val.is_undefined () ? sr.varval () : expr_val),
1063  is_automatic (sr.is_automatic ()),
1064  is_complex (varval.is_complex_type ()),
1065  is_formal (sr.is_formal ()),
1066  is_global (sr.is_global ()),
1067  is_persistent (sr.is_persistent ())
1068  { }
1069 
1070  void display_line (std::ostream& os,
1071  const std::list<whos_parameter>& params) const
1072  {
1073  std::string dims_str = get_dims_str (varval);
1074 
1075  std::list<whos_parameter>::const_iterator i = params.begin ();
1076 
1077  octave_preserve_stream_state stream_state (os);
1078 
1079  while (i != params.end ())
1080  {
1081  whos_parameter param = *i;
1082 
1083  if (param.command != '\0')
1084  {
1085  // Do the actual printing.
1086 
1087  switch (param.modifier)
1088  {
1089  case 'l':
1090  os << std::setiosflags (std::ios::left)
1091  << std::setw (param.parameter_length);
1092  break;
1093 
1094  case 'r':
1095  os << std::setiosflags (std::ios::right)
1096  << std::setw (param.parameter_length);
1097  break;
1098 
1099  case 'c':
1100  if (param.command == 's')
1101  {
1102  int front = param.first_parameter_length
1103  - dims_str.find ('x');
1104  int back = param.parameter_length
1105  - dims_str.length ()
1106  - front;
1107  front = (front > 0) ? front : 0;
1108  back = (back > 0) ? back : 0;
1109 
1110  os << std::setiosflags (std::ios::left)
1111  << std::setw (front)
1112  << ""
1113  << std::resetiosflags (std::ios::left)
1114  << dims_str
1115  << std::setiosflags (std::ios::left)
1116  << std::setw (back)
1117  << ""
1118  << std::resetiosflags (std::ios::left);
1119  }
1120  else
1121  {
1122  os << std::setiosflags (std::ios::left)
1123  << std::setw (param.parameter_length);
1124  }
1125  break;
1126 
1127  default:
1128  error ("whos_line_format: modifier '%c' unknown",
1129  param.modifier);
1130 
1131  os << std::setiosflags (std::ios::right)
1132  << std::setw (param.parameter_length);
1133  }
1134 
1135  switch (param.command)
1136  {
1137  case 'a':
1138  {
1139  char tmp[6];
1140 
1141  tmp[0] = (is_automatic ? 'a' : ' ');
1142  tmp[1] = (is_complex ? 'c' : ' ');
1143  tmp[2] = (is_formal ? 'f' : ' ');
1144  tmp[3] = (is_global ? 'g' : ' ');
1145  tmp[4] = (is_persistent ? 'p' : ' ');
1146  tmp[5] = 0;
1147 
1148  os << tmp;
1149  }
1150  break;
1151 
1152  case 'b':
1153  os << varval.byte_size ();
1154  break;
1155 
1156  case 'c':
1157  os << varval.class_name ();
1158  break;
1159 
1160  case 'e':
1161  os << varval.capacity ();
1162  break;
1163 
1164  case 'n':
1165  os << name;
1166  break;
1167 
1168  case 's':
1169  if (param.modifier != 'c')
1170  os << dims_str;
1171  break;
1172 
1173  case 't':
1174  os << varval.type_name ();
1175  break;
1176 
1177  default:
1178  error ("whos_line_format: command '%c' unknown",
1179  param.command);
1180  }
1181 
1182  os << std::resetiosflags (std::ios::left)
1183  << std::resetiosflags (std::ios::right);
1184  i++;
1185  }
1186  else
1187  {
1188  os << param.text;
1189  i++;
1190  }
1191  }
1192  }
1193 
1194  std::string name;
1201  };
1202 
1203 public:
1204  symbol_info_list (void) : lst () { }
1205 
1206  symbol_info_list (const symbol_info_list& sil) : lst (sil.lst) { }
1207 
1208  symbol_info_list& operator = (const symbol_info_list& sil)
1209  {
1210  if (this != &sil)
1211  lst = sil.lst;
1212 
1213  return *this;
1214  }
1215 
1217 
1218  void append (const symbol_table::symbol_record& sr)
1219  {
1220  lst.push_back (symbol_info (sr));
1221  }
1222 
1223  void append (const symbol_table::symbol_record& sr,
1224  const std::string& expr_str,
1225  const octave_value& expr_val)
1226  {
1227  lst.push_back (symbol_info (sr, expr_str, expr_val));
1228  }
1229 
1230  size_t size (void) const { return lst.size (); }
1231 
1232  bool empty (void) const { return lst.empty (); }
1233 
1234  octave_map
1235  map_value (const std::string& caller_function_name, int nesting_level) const
1236  {
1237  size_t len = lst.size ();
1238 
1239  Cell name_info (len, 1);
1240  Cell size_info (len, 1);
1241  Cell bytes_info (len, 1);
1242  Cell class_info (len, 1);
1243  Cell global_info (len, 1);
1244  Cell sparse_info (len, 1);
1245  Cell complex_info (len, 1);
1246  Cell nesting_info (len, 1);
1247  Cell persistent_info (len, 1);
1248 
1249  std::list<symbol_info>::const_iterator p = lst.begin ();
1250 
1251  for (size_t j = 0; j < len; j++)
1252  {
1253  const symbol_info& si = *p++;
1254 
1255  octave_scalar_map ni;
1256 
1257  ni.assign ("function", caller_function_name);
1258  ni.assign ("level", nesting_level);
1259 
1260  name_info(j) = si.name;
1261  global_info(j) = si.is_global;
1262  persistent_info(j) = si.is_persistent;
1263 
1264  octave_value val = si.varval;
1265 
1266  size_info(j) = val.size ();
1267  bytes_info(j) = val.byte_size ();
1268  class_info(j) = val.class_name ();
1269  sparse_info(j) = val.is_sparse_type ();
1270  complex_info(j) = val.is_complex_type ();
1271  nesting_info(j) = ni;
1272  }
1273 
1274  octave_map info;
1275 
1276  info.assign ("name", name_info);
1277  info.assign ("size", size_info);
1278  info.assign ("bytes", bytes_info);
1279  info.assign ("class", class_info);
1280  info.assign ("global", global_info);
1281  info.assign ("sparse", sparse_info);
1282  info.assign ("complex", complex_info);
1283  info.assign ("nesting", nesting_info);
1284  info.assign ("persistent", persistent_info);
1285 
1286  return info;
1287  }
1288 
1289  void display (std::ostream& os)
1290  {
1291  if (! lst.empty ())
1292  {
1293  size_t bytes = 0;
1294  size_t elements = 0;
1295 
1296  std::list<whos_parameter> params = parse_whos_line_format ();
1297 
1298  print_descriptor (os, params);
1299 
1300  octave_stdout << "\n";
1301 
1302  for (std::list<symbol_info>::const_iterator p = lst.begin ();
1303  p != lst.end (); p++)
1304  {
1305  p->display_line (os, params);
1306 
1307  octave_value val = p->varval;
1308 
1309  elements += val.capacity ();
1310  bytes += val.byte_size ();
1311  }
1312 
1313  os << "\nTotal is " << elements
1314  << (elements == 1 ? " element" : " elements")
1315  << " using " << bytes << (bytes == 1 ? " byte" : " bytes")
1316  << "\n";
1317  }
1318  }
1319 
1320  // Parse the string whos_line_format, and return a parameter list,
1321  // containing all information needed to print the given
1322  // attributtes of the symbols.
1323  std::list<whos_parameter> parse_whos_line_format (void)
1324  {
1325  int idx;
1326  size_t format_len = Vwhos_line_format.length ();
1327  char garbage;
1328  std::list<whos_parameter> params;
1329 
1330  size_t bytes1;
1331  int elements1;
1332 
1333  std::string param_string = "abcenst";
1334  Array<int> param_length (dim_vector (param_string.length (), 1));
1335  Array<std::string> param_names (dim_vector (param_string.length (), 1));
1336  size_t pos_a, pos_b, pos_c, pos_e, pos_n, pos_s, pos_t;
1337 
1338  pos_a = param_string.find ('a'); // Attributes
1339  pos_b = param_string.find ('b'); // Bytes
1340  pos_c = param_string.find ('c'); // Class
1341  pos_e = param_string.find ('e'); // Elements
1342  pos_n = param_string.find ('n'); // Name
1343  pos_s = param_string.find ('s'); // Size
1344  pos_t = param_string.find ('t'); // Type
1345 
1346  param_names(pos_a) = "Attr";
1347  param_names(pos_b) = "Bytes";
1348  param_names(pos_c) = "Class";
1349  param_names(pos_e) = "Elements";
1350  param_names(pos_n) = "Name";
1351  param_names(pos_s) = "Size";
1352  param_names(pos_t) = "Type";
1353 
1354  for (size_t i = 0; i < param_string.length (); i++)
1355  param_length(i) = param_names(i).length ();
1356 
1357  // The attribute column needs size 5.
1358  param_length(pos_a) = 5;
1359 
1360  // Calculating necessary spacing for name column,
1361  // bytes column, elements column and class column
1362 
1363  for (std::list<symbol_info>::const_iterator p = lst.begin ();
1364  p != lst.end (); p++)
1365  {
1366  std::stringstream ss1, ss2;
1367  std::string str;
1368 
1369  str = p->name;
1370  param_length(pos_n) = ((str.length ()
1371  > static_cast<size_t> (param_length(pos_n)))
1372  ? str.length () : param_length(pos_n));
1373 
1374  octave_value val = p->varval;
1375 
1376  str = val.type_name ();
1377  param_length(pos_t) = ((str.length ()
1378  > static_cast<size_t> (param_length(pos_t)))
1379  ? str.length () : param_length(pos_t));
1380 
1381  elements1 = val.capacity ();
1382  ss1 << elements1;
1383  str = ss1.str ();
1384  param_length(pos_e) = ((str.length ()
1385  > static_cast<size_t> (param_length(pos_e)))
1386  ? str.length () : param_length(pos_e));
1387 
1388  bytes1 = val.byte_size ();
1389  ss2 << bytes1;
1390  str = ss2.str ();
1391  param_length(pos_b) = ((str.length ()
1392  > static_cast<size_t> (param_length(pos_b)))
1393  ? str.length () : param_length (pos_b));
1394  }
1395 
1396  idx = 0;
1397  while (static_cast<size_t> (idx) < format_len)
1398  {
1399  whos_parameter param;
1400  param.command = '\0';
1401 
1402  if (Vwhos_line_format[idx] == '%')
1403  {
1404  bool error_encountered = false;
1405  param.modifier = 'r';
1406  param.parameter_length = 0;
1407 
1408  int a = 0, b = -1, balance = 1;
1409  unsigned int items;
1410  size_t pos;
1411  std::string cmd;
1412 
1413  // Parse one command from whos_line_format
1414  cmd = Vwhos_line_format.substr (idx, Vwhos_line_format.length ());
1415  pos = cmd.find (';');
1416  if (pos != std::string::npos)
1417  cmd = cmd.substr (0, pos+1);
1418  else
1419  error ("parameter without ; in whos_line_format");
1420 
1421  idx += cmd.length ();
1422 
1423  // FIXME: use iostream functions instead of sscanf!
1424 
1425  if (cmd.find_first_of ("crl") != 1)
1426  items = sscanf (cmd.c_str (), "%c%c:%d:%d:%d;",
1427  &garbage, &param.command, &a, &b, &balance);
1428  else
1429  items = sscanf (cmd.c_str (), "%c%c%c:%d:%d:%d;",
1430  &garbage, &param.modifier, &param.command,
1431  &a, &b, &balance) - 1;
1432 
1433  if (items < 2)
1434  {
1435  error ("whos_line_format: parameter structure without command in whos_line_format");
1436  error_encountered = true;
1437  }
1438 
1439  // Insert data into parameter
1440  param.first_parameter_length = 0;
1441  pos = param_string.find (param.command);
1442  if (pos != std::string::npos)
1443  {
1444  param.parameter_length = param_length(pos);
1445  param.text = param_names(pos);
1446  param.line.assign (param_names(pos).length (), '=');
1447 
1448  param.parameter_length = (a > param.parameter_length
1449  ? a : param.parameter_length);
1450  if (param.command == 's' && param.modifier == 'c' && b > 0)
1451  param.first_parameter_length = b;
1452  }
1453  else
1454  {
1455  error ("whos_line_format: '%c' is not a command",
1456  param.command);
1457  error_encountered = true;
1458  }
1459 
1460  if (param.command == 's')
1461  {
1462  // Have to calculate space needed for printing
1463  // matrix dimensions Space needed for Size column is
1464  // hard to determine in prior, because it depends on
1465  // dimensions to be shown. That is why it is
1466  // recalculated for each Size-command int first,
1467  // rest = 0, total;
1468  int rest = 0;
1469  int first = param.first_parameter_length;
1470  int total = param.parameter_length;
1471 
1472  for (std::list<symbol_info>::const_iterator p = lst.begin ();
1473  p != lst.end (); p++)
1474  {
1475  octave_value val = p->varval;
1476  std::string dims_str = get_dims_str (val);
1477  int first1 = dims_str.find ('x');
1478  int total1 = dims_str.length ();
1479  int rest1 = total1 - first1;
1480  rest = (rest1 > rest ? rest1 : rest);
1481  first = (first1 > first ? first1 : first);
1482  total = (total1 > total ? total1 : total);
1483  }
1484 
1485  if (param.modifier == 'c')
1486  {
1487  if (first < balance)
1488  first += balance - first;
1489  if (rest + balance < param.parameter_length)
1490  rest += param.parameter_length - rest - balance;
1491 
1492  param.parameter_length = first + rest;
1493  param.first_parameter_length = first;
1494  param.balance = balance;
1495  }
1496  else
1497  {
1498  param.parameter_length = total;
1499  param.first_parameter_length = 0;
1500  }
1501  }
1502  else if (param.modifier == 'c')
1503  {
1504  error ("whos_line_format: modifier 'c' not available for command '%c'",
1505  param.command);
1506  error_encountered = true;
1507  }
1508 
1509  // What happens if whos_line_format contains negative numbers
1510  // at param_length positions?
1511  param.balance = (b < 0 ? 0 : param.balance);
1512  param.first_parameter_length = (b < 0 ? 0 :
1513  param.first_parameter_length);
1514  param.parameter_length = (a < 0
1515  ? 0
1516  : (param.parameter_length
1517  < param_length(pos_s)
1518  ? param_length(pos_s)
1519  : param.parameter_length));
1520 
1521  // Parameter will not be pushed into parameter list if ...
1522  if (! error_encountered)
1523  params.push_back (param);
1524  }
1525  else
1526  {
1527  // Text string, to be printed as it is ...
1528  std::string text;
1529  size_t pos;
1530  text = Vwhos_line_format.substr (idx, Vwhos_line_format.length ());
1531  pos = text.find ('%');
1532  if (pos != std::string::npos)
1533  text = text.substr (0, pos);
1534 
1535  // Push parameter into list ...
1536  idx += text.length ();
1537  param.text=text;
1538  param.line.assign (text.length (), ' ');
1539  params.push_back (param);
1540  }
1541  }
1542 
1543  return params;
1544  }
1545 
1546 private:
1547  std::list<symbol_info> lst;
1548 
1549 };
1550 
1551 static octave_value
1552 do_who (int argc, const string_vector& argv, bool return_list,
1553  bool verbose = false, std::string msg = std::string ())
1554 {
1555  octave_value retval;
1556 
1557  std::string my_name = argv[0];
1558 
1559  bool global_only = false;
1560  bool have_regexp = false;
1561 
1562  int i;
1563  for (i = 1; i < argc; i++)
1564  {
1565  if (argv[i] == "-file")
1566  {
1567  // FIXME: This is an inefficient manner to implement this as the
1568  // variables are loaded in to a temporary context and then treated.
1569  // It would be better to refecat symbol_info_list to not store the
1570  // symbol records and then use it in load-save.cc (do_load) to
1571  // implement this option there so that the variables are never
1572  // stored at all.
1573  if (i == argc - 1)
1574  error ("whos: -file argument must be followed by a file name");
1575  else
1576  {
1577  std::string nm = argv[i + 1];
1578 
1579  unwind_protect frame;
1580 
1581  // Set up temporary scope.
1582 
1584  frame.add_fcn (symbol_table::erase_scope, tmp_scope);
1585 
1586  symbol_table::set_scope (tmp_scope);
1587 
1588  octave_call_stack::push (tmp_scope, 0);
1590 
1592 
1593  feval ("load", octave_value (nm), 0);
1594 
1595  if (! error_state)
1596  {
1597  std::string newmsg = std::string ("Variables in the file ") +
1598  nm + ":\n\n";
1599 
1600  retval = do_who (i, argv, return_list, verbose, newmsg);
1601  }
1602  }
1603 
1604  return retval;
1605  }
1606  else if (argv[i] == "-regexp")
1607  have_regexp = true;
1608  else if (argv[i] == "global")
1609  global_only = true;
1610  else if (argv[i][0] == '-')
1611  warning ("%s: unrecognized option '%s'", my_name.c_str (),
1612  argv[i].c_str ());
1613  else
1614  break;
1615  }
1616 
1617  int npats = argc - i;
1618  string_vector pats;
1619  if (npats > 0)
1620  {
1621  pats.resize (npats);
1622  for (int j = 0; j < npats; j++)
1623  pats[j] = argv[i+j];
1624  }
1625  else
1626  {
1627  pats.resize (++npats);
1628  pats[0] = "*";
1629  }
1630 
1631  symbol_info_list symbol_stats;
1632  std::list<std::string> symbol_names;
1633 
1634  for (int j = 0; j < npats; j++)
1635  {
1636  std::string pat = pats[j];
1637 
1638  if (have_regexp)
1639  {
1640  std::list<symbol_table::symbol_record> tmp = global_only
1643 
1644  for (std::list<symbol_table::symbol_record>::const_iterator
1645  p = tmp.begin (); p != tmp.end (); p++)
1646  {
1647  if (p->is_variable ())
1648  {
1649  if (verbose)
1650  symbol_stats.append (*p);
1651  else
1652  symbol_names.push_back (p->name ());
1653  }
1654  }
1655  }
1656  else
1657  {
1658  size_t pos = pat.find_first_of (".({");
1659 
1660  if (pos != std::string::npos && pos > 0)
1661  {
1662  if (verbose)
1663  {
1664  // NOTE: we can only display information for
1665  // expressions based on global values if the variable is
1666  // global in the current scope because we currently have
1667  // no way of looking up the base value in the global
1668  // scope and then evaluating the arguments in the
1669  // current scope.
1670 
1671  std::string base_name = pat.substr (0, pos);
1672 
1673  if (symbol_table::is_variable (base_name))
1674  {
1676  = symbol_table::find_symbol (base_name);
1677 
1678  if (! global_only || sr.is_global ())
1679  {
1680  int parse_status;
1681 
1682  octave_value expr_val
1683  = eval_string (pat, true, parse_status);
1684 
1685  if (! error_state)
1686  symbol_stats.append (sr, pat, expr_val);
1687  else
1688  return retval;
1689  }
1690  }
1691  }
1692  }
1693  else
1694  {
1695  std::list<symbol_table::symbol_record> tmp = global_only
1698 
1699  for (std::list<symbol_table::symbol_record>::const_iterator
1700  p = tmp.begin (); p != tmp.end (); p++)
1701  {
1702  if (p->is_variable ())
1703  {
1704  if (verbose)
1705  symbol_stats.append (*p);
1706  else
1707  symbol_names.push_back (p->name ());
1708  }
1709  }
1710  }
1711  }
1712  }
1713 
1714  if (return_list)
1715  {
1716  if (verbose)
1717  {
1718  std::string caller_function_name;
1720  if (caller)
1721  caller_function_name = caller->name ();
1722 
1723  retval = symbol_stats.map_value (caller_function_name, 1);
1724  }
1725  else
1726  retval = Cell (string_vector (symbol_names));
1727  }
1728  else if (! (symbol_stats.empty () && symbol_names.empty ()))
1729  {
1730  if (msg.length () == 0)
1731  if (global_only)
1732  octave_stdout << "Global variables:\n\n";
1733  else
1734  octave_stdout << "Variables in the current scope:\n\n";
1735  else
1736  octave_stdout << msg;
1737 
1738  if (verbose)
1739  symbol_stats.display (octave_stdout);
1740  else
1741  {
1742  string_vector names (symbol_names);
1743 
1745  }
1746 
1747  octave_stdout << "\n";
1748  }
1749 
1750  return retval;
1751 }
1752 
1753 DEFUN (who, args, nargout,
1754  "-*- texinfo -*-\n\
1755 @deftypefn {Command} {} who\n\
1756 @deftypefnx {Command} {} who pattern @dots{}\n\
1757 @deftypefnx {Command} {} who option pattern @dots{}\n\
1758 @deftypefnx {Command} {C =} who (\"pattern\", @dots{})\n\
1759 List currently defined variables matching the given patterns. Valid\n\
1760 pattern syntax is the same as described for the @code{clear} command.\n\
1761 If no patterns are supplied, all variables are listed.\n\
1762 By default, only variables visible in the local scope are displayed.\n\
1763 \n\
1764 The following are valid options but may not be combined.\n\
1765 \n\
1766 @table @code\n\
1767 @item global\n\
1768 List variables in the global scope rather than the current scope.\n\
1769 \n\
1770 @item -regexp\n\
1771 The patterns are considered to be regular expressions when matching the\n\
1772 variables to display. The same pattern syntax accepted by\n\
1773 the @code{regexp} function is used.\n\
1774 \n\
1775 @item -file\n\
1776 The next argument is treated as a filename. All variables found within the\n\
1777 specified file are listed. No patterns are accepted when reading variables\n\
1778 from a file.\n\
1779 @end table\n\
1780 \n\
1781 If called as a function, return a cell array of defined variable names\n\
1782 matching the given patterns.\n\
1783 @seealso{whos, isglobal, isvarname, exist, regexp}\n\
1784 @end deftypefn")
1785 {
1786  octave_value retval;
1787 
1788  if (nargout < 2)
1789  {
1790  int argc = args.length () + 1;
1791 
1792  string_vector argv = args.make_argv ("who");
1793 
1794  if (! error_state)
1795  retval = do_who (argc, argv, nargout == 1);
1796  }
1797  else
1798  print_usage ();
1799 
1800  return retval;
1801 }
1802 
1803 DEFUN (whos, args, nargout,
1804  "-*- texinfo -*-\n\
1805 @deftypefn {Command} {} whos\n\
1806 @deftypefnx {Command} {} whos pattern @dots{}\n\
1807 @deftypefnx {Command} {} whos option pattern @dots{}\n\
1808 @deftypefnx {Command} {S =} whos (\"pattern\", @dots{})\n\
1809 Provide detailed information on currently defined variables matching the\n\
1810 given patterns. Options and pattern syntax are the same as for the\n\
1811 @code{who} command. Extended information about each variable is\n\
1812 summarized in a table with the following default entries.\n\
1813 \n\
1814 @table @asis\n\
1815 @item Attr\n\
1816 Attributes of the listed variable. Possible attributes are:\n\
1817 \n\
1818 @table @asis\n\
1819 @item blank\n\
1820 Variable in local scope\n\
1821 \n\
1822 @item @code{a}\n\
1823 Automatic variable. An automatic variable is one created by the\n\
1824 interpreter, for example @code{argn}.\n\
1825 \n\
1826 @item @code{c}\n\
1827 Variable of complex type.\n\
1828 \n\
1829 @item @code{f}\n\
1830 Formal parameter (function argument).\n\
1831 \n\
1832 @item @code{g}\n\
1833 Variable with global scope.\n\
1834 \n\
1835 @item @code{p}\n\
1836 Persistent variable.\n\
1837 @end table\n\
1838 \n\
1839 @item Name\n\
1840 The name of the variable.\n\
1841 \n\
1842 @item Size\n\
1843 The logical size of the variable. A scalar is 1x1, a vector is\n\
1844 @nospell{1xN} or @nospell{Nx1}, a 2-D matrix is @nospell{MxN}.\n\
1845 \n\
1846 @item Bytes\n\
1847 The amount of memory currently used to store the variable.\n\
1848 \n\
1849 @item Class\n\
1850 The class of the variable. Examples include double, single, char, uint16,\n\
1851 cell, and struct.\n\
1852 @end table\n\
1853 \n\
1854 The table can be customized to display more or less information through\n\
1855 the function @code{whos_line_format}.\n\
1856 \n\
1857 If @code{whos} is called as a function, return a struct array of defined\n\
1858 variable names matching the given patterns. Fields in the structure\n\
1859 describing each variable are: name, size, bytes, class, global, sparse,\n\
1860 complex, nesting, persistent.\n\
1861 @seealso{who, whos_line_format}\n\
1862 @end deftypefn")
1863 {
1864  octave_value retval;
1865 
1866  if (nargout < 2)
1867  {
1868  int argc = args.length () + 1;
1869 
1870  string_vector argv = args.make_argv ("whos");
1871 
1872  if (! error_state)
1873  retval = do_who (argc, argv, nargout == 1, true);
1874  }
1875  else
1876  print_usage ();
1877 
1878  return retval;
1879 }
1880 
1881 // Defining variables.
1882 
1883 void
1884 bind_ans (const octave_value& val, bool print)
1885 {
1886  static std::string ans = "ans";
1887 
1888  if (val.is_defined ())
1889  {
1890  if (val.is_cs_list ())
1891  {
1892  octave_value_list lst = val.list_value ();
1893 
1894  for (octave_idx_type i = 0; i < lst.length (); i++)
1895  bind_ans (lst(i), print);
1896  }
1897  else
1898  {
1899  symbol_table::force_assign (ans, val);
1900 
1901  if (print)
1902  val.print_with_name (octave_stdout, ans);
1903  }
1904  }
1905 }
1906 
1907 void
1908 bind_internal_variable (const std::string& fname, const octave_value& val)
1909 {
1910  octave_value_list args;
1911 
1912  args(0) = val;
1913 
1914  feval (fname, args, 0);
1915 }
1916 
1917 void
1918 mlock (void)
1919 {
1921 
1922  if (fcn)
1923  fcn->lock ();
1924  else
1925  error ("mlock: invalid use outside a function");
1926 }
1927 
1928 void
1929 munlock (const std::string& nm)
1930 {
1932 
1933  if (val.is_defined ())
1934  {
1935  octave_function *fcn = val.function_value ();
1936 
1937  if (fcn)
1938  fcn->unlock ();
1939  }
1940 }
1941 
1942 bool
1943 mislocked (const std::string& nm)
1944 {
1945  bool retval = false;
1946 
1948 
1949  if (val.is_defined ())
1950  {
1951  octave_function *fcn = val.function_value ();
1952 
1953  if (fcn)
1954  retval = fcn->islocked ();
1955  }
1956 
1957  return retval;
1958 }
1959 
1960 DEFUN (mlock, args, ,
1961  "-*- texinfo -*-\n\
1962 @deftypefn {Built-in Function} {} mlock ()\n\
1963 Lock the current function into memory so that it can't be cleared.\n\
1964 @seealso{munlock, mislocked, persistent}\n\
1965 @end deftypefn")
1966 {
1967  octave_value_list retval;
1968 
1969  if (args.length () == 0)
1970  {
1972 
1973  if (fcn)
1974  fcn->lock ();
1975  else
1976  error ("mlock: invalid use outside a function");
1977  }
1978  else
1979  print_usage ();
1980 
1981  return retval;
1982 }
1983 
1984 DEFUN (munlock, args, ,
1985  "-*- texinfo -*-\n\
1986 @deftypefn {Built-in Function} {} munlock ()\n\
1987 @deftypefnx {Built-in Function} {} munlock (@var{fcn})\n\
1988 Unlock the named function @var{fcn}. If no function is named\n\
1989 then unlock the current function.\n\
1990 @seealso{mlock, mislocked, persistent}\n\
1991 @end deftypefn")
1992 {
1993  octave_value_list retval;
1994 
1995  if (args.length () == 1)
1996  {
1997  std::string name = args(0).string_value ();
1998 
1999  if (! error_state)
2000  munlock (name);
2001  else
2002  error ("munlock: FCN must be a string");
2003  }
2004  else if (args.length () == 0)
2005  {
2007 
2008  if (fcn)
2009  fcn->unlock ();
2010  else
2011  error ("munlock: invalid use outside a function");
2012  }
2013  else
2014  print_usage ();
2015 
2016  return retval;
2017 }
2018 
2019 
2020 DEFUN (mislocked, args, ,
2021  "-*- texinfo -*-\n\
2022 @deftypefn {Built-in Function} {} mislocked ()\n\
2023 @deftypefnx {Built-in Function} {} mislocked (@var{fcn})\n\
2024 Return true if the named function @var{fcn} is locked. If no function is\n\
2025 named then return true if the current function is locked.\n\
2026 @seealso{mlock, munlock, persistent}\n\
2027 @end deftypefn")
2028 {
2029  octave_value retval;
2030 
2031  if (args.length () == 1)
2032  {
2033  std::string name = args(0).string_value ();
2034 
2035  if (! error_state)
2036  retval = mislocked (name);
2037  else
2038  error ("mislocked: FCN must be a string");
2039  }
2040  else if (args.length () == 0)
2041  {
2043 
2044  if (fcn)
2045  retval = fcn->islocked ();
2046  else
2047  error ("mislocked: invalid use outside a function");
2048  }
2049  else
2050  print_usage ();
2051 
2052  return retval;
2053 }
2054 
2055 // Deleting names from the symbol tables.
2056 
2057 static inline bool
2058 name_matches_any_pattern (const std::string& nm, const string_vector& argv,
2059  int argc, int idx, bool have_regexp = false)
2060 {
2061  bool retval = false;
2062 
2063  for (int k = idx; k < argc; k++)
2064  {
2065  std::string patstr = argv[k];
2066  if (! patstr.empty ())
2067  {
2068  if (have_regexp)
2069  {
2070  if (is_regexp_match (patstr, nm))
2071  {
2072  retval = true;
2073  break;
2074  }
2075  }
2076  else
2077  {
2078  glob_match pattern (patstr);
2079 
2080  if (pattern.match (nm))
2081  {
2082  retval = true;
2083  break;
2084  }
2085  }
2086  }
2087  }
2088 
2089  return retval;
2090 }
2091 
2092 static inline void
2093 maybe_warn_exclusive (bool exclusive)
2094 {
2095  if (exclusive)
2096  warning ("clear: ignoring --exclusive option");
2097 }
2098 
2099 static void
2100 do_clear_functions (const string_vector& argv, int argc, int idx,
2101  bool exclusive = false)
2102 {
2103  if (idx == argc)
2105  else
2106  {
2107  if (exclusive)
2108  {
2110 
2111  int fcount = fcns.length ();
2112 
2113  for (int i = 0; i < fcount; i++)
2114  {
2115  std::string nm = fcns[i];
2116 
2117  if (! name_matches_any_pattern (nm, argv, argc, idx))
2119  }
2120  }
2121  else
2122  {
2123  while (idx < argc)
2125  }
2126  }
2127 }
2128 
2129 static void
2130 do_clear_globals (const string_vector& argv, int argc, int idx,
2131  bool exclusive = false)
2132 {
2133  if (idx == argc)
2134  {
2136 
2137  int gcount = gvars.length ();
2138 
2139  for (int i = 0; i < gcount; i++)
2140  symbol_table::clear_global (gvars[i]);
2141  }
2142  else
2143  {
2144  if (exclusive)
2145  {
2147 
2148  int gcount = gvars.length ();
2149 
2150  for (int i = 0; i < gcount; i++)
2151  {
2152  std::string nm = gvars[i];
2153 
2154  if (! name_matches_any_pattern (nm, argv, argc, idx))
2156  }
2157  }
2158  else
2159  {
2160  while (idx < argc)
2161  symbol_table::clear_global_pattern (argv[idx++]);
2162  }
2163  }
2164 }
2165 
2166 static void
2167 do_clear_variables (const string_vector& argv, int argc, int idx,
2168  bool exclusive = false, bool have_regexp = false)
2169 {
2170  if (idx == argc)
2172  else
2173  {
2174  if (exclusive)
2175  {
2177 
2178  int lcount = lvars.length ();
2179 
2180  for (int i = 0; i < lcount; i++)
2181  {
2182  std::string nm = lvars[i];
2183 
2184  if (! name_matches_any_pattern (nm, argv, argc, idx, have_regexp))
2186  }
2187  }
2188  else
2189  {
2190  if (have_regexp)
2191  while (idx < argc)
2193  else
2194  while (idx < argc)
2196  }
2197  }
2198 }
2199 
2200 static void
2201 do_clear_symbols (const string_vector& argv, int argc, int idx,
2202  bool exclusive = false)
2203 {
2204  if (idx == argc)
2206  else
2207  {
2208  if (exclusive)
2209  {
2210  // FIXME: is this really what we want, or do we
2211  // somehow want to only clear the functions that are not
2212  // shadowed by local variables? It seems that would be a
2213  // bit harder to do.
2214 
2215  do_clear_variables (argv, argc, idx, exclusive);
2216  do_clear_functions (argv, argc, idx, exclusive);
2217  }
2218  else
2219  {
2220  while (idx < argc)
2221  symbol_table::clear_symbol_pattern (argv[idx++]);
2222  }
2223  }
2224 }
2225 
2226 static void
2227 do_matlab_compatible_clear (const string_vector& argv, int argc, int idx)
2228 {
2229  // This is supposed to be mostly Matlab compatible.
2230 
2231  for (; idx < argc; idx++)
2232  {
2233  if (argv[idx] == "all"
2234  && ! symbol_table::is_local_variable ("all"))
2235  {
2237  }
2238  else if (argv[idx] == "functions"
2239  && ! symbol_table::is_local_variable ("functions"))
2240  {
2241  do_clear_functions (argv, argc, ++idx);
2242  }
2243  else if (argv[idx] == "global"
2244  && ! symbol_table::is_local_variable ("global"))
2245  {
2246  do_clear_globals (argv, argc, ++idx);
2247  }
2248  else if (argv[idx] == "variables"
2249  && ! symbol_table::is_local_variable ("variables"))
2250  {
2252  }
2253  else if (argv[idx] == "classes"
2254  && ! symbol_table::is_local_variable ("classes"))
2255  {
2258  }
2259  else
2260  {
2262  }
2263  }
2264 }
2265 
2266 #define CLEAR_OPTION_ERROR(cond) \
2267  do \
2268  { \
2269  if (cond) \
2270  { \
2271  print_usage (); \
2272  return retval; \
2273  } \
2274  } \
2275  while (0)
2276 
2277 DEFUN (clear, args, ,
2278  "-*- texinfo -*-\n\
2279 @deftypefn {Command} {} clear [options] pattern @dots{}\n\
2280 Delete the names matching the given patterns from the symbol table. The\n\
2281 pattern may contain the following special characters:\n\
2282 \n\
2283 @table @code\n\
2284 @item ?\n\
2285 Match any single character.\n\
2286 \n\
2287 @item *\n\
2288 Match zero or more characters.\n\
2289 \n\
2290 @item [ @var{list} ]\n\
2291 Match the list of characters specified by @var{list}. If the first\n\
2292 character is @code{!} or @code{^}, match all characters except those\n\
2293 specified by @var{list}. For example, the pattern @samp{[a-zA-Z]} will\n\
2294 match all lowercase and uppercase alphabetic characters.\n\
2295 @end table\n\
2296 \n\
2297 For example, the command\n\
2298 \n\
2299 @example\n\
2300 clear foo b*r\n\
2301 @end example\n\
2302 \n\
2303 @noindent\n\
2304 clears the name @code{foo} and all names that begin with the letter\n\
2305 @code{b} and end with the letter @code{r}.\n\
2306 \n\
2307 If @code{clear} is called without any arguments, all user-defined\n\
2308 variables (local and global) are cleared from the symbol table. If\n\
2309 @code{clear} is called with at least one argument, only the visible\n\
2310 names matching the arguments are cleared. For example, suppose you have\n\
2311 defined a function @code{foo}, and then hidden it by performing the\n\
2312 assignment @code{foo = 2}. Executing the command @kbd{clear foo} once\n\
2313 will clear the variable definition and restore the definition of\n\
2314 @code{foo} as a function. Executing @kbd{clear foo} a second time will\n\
2315 clear the function definition.\n\
2316 \n\
2317 The following options are available in both long and short form\n\
2318 \n\
2319 @table @code\n\
2320 @item -all, -a\n\
2321 Clears all local and global user-defined variables and all functions\n\
2322 from the symbol table.\n\
2323 \n\
2324 @item -exclusive, -x\n\
2325 Clears the variables that don't match the following pattern.\n\
2326 \n\
2327 @item -functions, -f\n\
2328 Clears the function names and the built-in symbols names.\n\
2329 \n\
2330 @item -global, -g\n\
2331 Clears the global symbol names.\n\
2332 \n\
2333 @item -variables, -v\n\
2334 Clears the local variable names.\n\
2335 \n\
2336 @item -classes, -c\n\
2337 Clears the class structure table and clears all objects.\n\
2338 \n\
2339 @item -regexp, -r\n\
2340 The arguments are treated as regular expressions as any variables that\n\
2341 match will be cleared.\n\
2342 @end table\n\
2343 \n\
2344 With the exception of @code{exclusive}, all long options can be used\n\
2345 without the dash as well.\n\
2346 @end deftypefn")
2347 {
2348  octave_value_list retval;
2349 
2350  int argc = args.length () + 1;
2351 
2352  string_vector argv = args.make_argv ("clear");
2353 
2354  if (! error_state)
2355  {
2356  if (argc == 1)
2357  {
2358  do_clear_globals (argv, argc, true);
2359  do_clear_variables (argv, argc, true);
2360 
2362  }
2363  else
2364  {
2365  int idx = 0;
2366 
2367  bool clear_all = false;
2368  bool clear_functions = false;
2369  bool clear_globals = false;
2370  bool clear_variables = false;
2371  bool clear_objects = false;
2372  bool exclusive = false;
2373  bool have_regexp = false;
2374  bool have_dash_option = false;
2375 
2376  while (++idx < argc)
2377  {
2378  if (argv[idx] == "-all" || argv[idx] == "-a")
2379  {
2380  CLEAR_OPTION_ERROR (have_dash_option && ! exclusive);
2381 
2382  have_dash_option = true;
2383  clear_all = true;
2384  }
2385  else if (argv[idx] == "-exclusive" || argv[idx] == "-x")
2386  {
2387  have_dash_option = true;
2388  exclusive = true;
2389  }
2390  else if (argv[idx] == "-functions" || argv[idx] == "-f")
2391  {
2392  CLEAR_OPTION_ERROR (have_dash_option && ! exclusive);
2393 
2394  have_dash_option = true;
2395  clear_functions = true;
2396  }
2397  else if (argv[idx] == "-global" || argv[idx] == "-g")
2398  {
2399  CLEAR_OPTION_ERROR (have_dash_option && ! exclusive);
2400 
2401  have_dash_option = true;
2402  clear_globals = true;
2403  }
2404  else if (argv[idx] == "-variables" || argv[idx] == "-v")
2405  {
2406  CLEAR_OPTION_ERROR (have_dash_option && ! exclusive);
2407 
2408  have_dash_option = true;
2409  clear_variables = true;
2410  }
2411  else if (argv[idx] == "-classes" || argv[idx] == "-c")
2412  {
2413  CLEAR_OPTION_ERROR (have_dash_option && ! exclusive);
2414 
2415  have_dash_option = true;
2416  clear_objects = true;
2417  }
2418  else if (argv[idx] == "-regexp" || argv[idx] == "-r")
2419  {
2420  CLEAR_OPTION_ERROR (have_dash_option && ! exclusive);
2421 
2422  have_dash_option = true;
2423  have_regexp = true;
2424  }
2425  else
2426  break;
2427  }
2428 
2429  if (idx <= argc)
2430  {
2431  if (! have_dash_option)
2432  {
2433  do_matlab_compatible_clear (argv, argc, idx);
2434  }
2435  else
2436  {
2437  if (clear_all)
2438  {
2439  maybe_warn_exclusive (exclusive);
2440 
2441  if (++idx < argc)
2442  warning
2443  ("clear: ignoring extra arguments after -all");
2444 
2446  }
2447  else if (have_regexp)
2448  {
2449  do_clear_variables (argv, argc, idx, exclusive, true);
2450  }
2451  else if (clear_functions)
2452  {
2453  do_clear_functions (argv, argc, idx, exclusive);
2454  }
2455  else if (clear_globals)
2456  {
2457  do_clear_globals (argv, argc, idx, exclusive);
2458  }
2459  else if (clear_variables)
2460  {
2461  do_clear_variables (argv, argc, idx, exclusive);
2462  }
2463  else if (clear_objects)
2464  {
2467  }
2468  else
2469  {
2470  do_clear_symbols (argv, argc, idx, exclusive);
2471  }
2472  }
2473 
2475  }
2476  }
2477  }
2478 
2479  return retval;
2480 }
2481 
2482 DEFUN (whos_line_format, args, nargout,
2483  "-*- texinfo -*-\n\
2484 @deftypefn {Built-in Function} {@var{val} =} whos_line_format ()\n\
2485 @deftypefnx {Built-in Function} {@var{old_val} =} whos_line_format (@var{new_val})\n\
2486 @deftypefnx {Built-in Function} {} whos_line_format (@var{new_val}, \"local\")\n\
2487 Query or set the format string used by the command @code{whos}.\n\
2488 \n\
2489 A full format string is:\n\
2490 @c Set example in small font to prevent overfull line\n\
2491 \n\
2492 @smallexample\n\
2493 %[modifier]<command>[:width[:left-min[:balance]]];\n\
2494 @end smallexample\n\
2495 \n\
2496 The following command sequences are available:\n\
2497 \n\
2498 @table @code\n\
2499 @item %a\n\
2500 Prints attributes of variables (g=global, p=persistent,\n\
2501 f=formal parameter, a=automatic variable).\n\
2502 \n\
2503 @item %b\n\
2504 Prints number of bytes occupied by variables.\n\
2505 \n\
2506 @item %c\n\
2507 Prints class names of variables.\n\
2508 \n\
2509 @item %e\n\
2510 Prints elements held by variables.\n\
2511 \n\
2512 @item %n\n\
2513 Prints variable names.\n\
2514 \n\
2515 @item %s\n\
2516 Prints dimensions of variables.\n\
2517 \n\
2518 @item %t\n\
2519 Prints type names of variables.\n\
2520 @end table\n\
2521 \n\
2522 Every command may also have an alignment modifier:\n\
2523 \n\
2524 @table @code\n\
2525 @item l\n\
2526 Left alignment.\n\
2527 \n\
2528 @item r\n\
2529 Right alignment (default).\n\
2530 \n\
2531 @item c\n\
2532 Column-aligned (only applicable to command %s).\n\
2533 @end table\n\
2534 \n\
2535 The @code{width} parameter is a positive integer specifying the minimum\n\
2536 number of columns used for printing. No maximum is needed as the field will\n\
2537 auto-expand as required.\n\
2538 \n\
2539 The parameters @code{left-min} and @code{balance} are only available when the\n\
2540 column-aligned modifier is used with the command @samp{%s}.\n\
2541 @code{balance} specifies the column number within the field width which will\n\
2542 be aligned between entries. Numbering starts from 0 which indicates the\n\
2543 leftmost column. @code{left-min} specifies the minimum field width to the\n\
2544 left of the specified balance column.\n\
2545 \n\
2546 The default format is\n\
2547 @qcode{\" %a:4; %ln:6; %cs:16:6:1; %rb:12; %lc:-1;\\n\"}.\n\
2548 \n\
2549 When called from inside a function with the @qcode{\"local\"} option, the\n\
2550 variable is changed locally for the function and any subroutines it calls. \n\
2551 The original variable value is restored when exiting the function.\n\
2552 @seealso{whos}\n\
2553 @end deftypefn")
2554 {
2555  return SET_INTERNAL_VARIABLE (whos_line_format);
2556 }
2557 
2558 static std::string Vmissing_function_hook = "__unimplemented__";
2559 
2560 DEFUN (missing_function_hook, args, nargout,
2561  "-*- texinfo -*-\n\
2562 @deftypefn {Built-in Function} {@var{val} =} missing_function_hook ()\n\
2563 @deftypefnx {Built-in Function} {@var{old_val} =} missing_function_hook (@var{new_val})\n\
2564 @deftypefnx {Built-in Function} {} missing_function_hook (@var{new_val}, \"local\")\n\
2565 Query or set the internal variable that specifies the function to call when\n\
2566 an unknown identifier is requested.\n\
2567 \n\
2568 When called from inside a function with the @qcode{\"local\"} option, the\n\
2569 variable is changed locally for the function and any subroutines it calls. \n\
2570 The original variable value is restored when exiting the function.\n\
2571 @seealso{missing_component_hook}\n\
2572 @end deftypefn")
2573 {
2574  return SET_INTERNAL_VARIABLE (missing_function_hook);
2575 }
2576 
2577 void maybe_missing_function_hook (const std::string& name)
2578 {
2579  // Don't do this if we're handling errors.
2580  if (buffer_error_messages == 0 && ! Vmissing_function_hook.empty ())
2581  {
2582  octave_value val = symbol_table::find_function (Vmissing_function_hook);
2583 
2584  if (val.is_defined ())
2585  {
2586  // Ensure auto-restoration.
2587  unwind_protect frame;
2588  frame.protect_var (Vmissing_function_hook);
2589 
2590  // Clear the variable prior to calling the function.
2591  const std::string func_name = Vmissing_function_hook;
2592  Vmissing_function_hook.clear ();
2593 
2594  // Call.
2595  feval (func_name, octave_value (name));
2596  }
2597  }
2598 }
2599 
2600 DEFUN (__varval__, args, ,
2601  "-*- texinfo -*-\n\
2602 @deftypefn {Built-in Function} {} __varval__ (@var{name})\n\
2603 Undocumented internal function.\n\
2604 @end deftypefn")
2605 {
2606  octave_value retval;
2607 
2608  if (args.length () == 1)
2609  {
2610  std::string name = args(0).string_value ();
2611 
2612  if (! error_state)
2613  retval = symbol_table::varval (args(0).string_value ());
2614  else
2615  error ("__varval__: expecting argument to be variable name");
2616  }
2617  else
2618  print_usage ();
2619 
2620  return retval;
2621 }
2622 
2623 static std::string Vmissing_component_hook;
2624 
2625 DEFUN (missing_component_hook, args, nargout,
2626  "-*- texinfo -*-\n\
2627 @deftypefn {Built-in Function} {@var{val} =} missing_component_hook ()\n\
2628 @deftypefnx {Built-in Function} {@var{old_val} =} missing_component_hook (@var{new_val})\n\
2629 @deftypefnx {Built-in Function} {} missing_component_hook (@var{new_val}, \"local\")\n\
2630 Query or set the internal variable that specifies the function to call when\n\
2631 a component of Octave is missing. This can be useful for packagers that\n\
2632 may split the Octave installation into multiple sub-packages, for example,\n\
2633 to provide a hint to users for how to install the missing components.\n\
2634 \n\
2635 When called from inside a function with the @qcode{\"local\"} option, the\n\
2636 variable is changed locally for the function and any subroutines it calls. \n\
2637 The original variable value is restored when exiting the function.\n\
2638 \n\
2639 The hook function is expected to be of the form\n\
2640 \n\
2641 @example\n\
2642 @var{fcn} (@var{component})\n\
2643 @end example\n\
2644 \n\
2645 Octave will call @var{fcn} with the name of the function that requires the\n\
2646 component and a string describing the missing component. The hook function\n\
2647 should return an error message to be displayed.\n\
2648 @seealso{missing_function_hook}\n\
2649 @end deftypefn")
2650 {
2651  return SET_INTERNAL_VARIABLE (missing_component_hook);
2652 }