GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
dirfns.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1994-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <cerrno>
31 #include <cstdio>
32 #include <cstddef>
33 #include <cstdlib>
34 #include <cstring>
35 
36 #include <sstream>
37 #include <string>
38 
39 #include "file-ops.h"
40 #include "file-stat.h"
41 #include "glob-match.h"
42 #include "oct-env.h"
43 #include "oct-glob.h"
44 #include "pathsearch.h"
45 #include "str-vec.h"
46 
47 #include "Cell.h"
48 #include "defun.h"
49 #include "dir-ops.h"
50 #include "error.h"
51 #include "errwarn.h"
52 #include "event-manager.h"
53 #include "input.h"
54 #include "load-path.h"
55 #include "octave.h"
56 #include "ovl.h"
57 #include "pager.h"
58 #include "procstream.h"
59 #include "sysdep.h"
60 #include "interpreter.h"
61 #include "unwind-prot.h"
62 #include "utils.h"
63 #include "variables.h"
64 
65 // TRUE means we ask for confirmation before recursively removing a
66 // directory tree.
67 static bool Vconfirm_recursive_rmdir = true;
68 
69 DEFMETHOD (cd, interp, args, nargout,
70  doc: /* -*- texinfo -*-
71 @deftypefn {} {} cd @var{dir}
72 @deftypefnx {} {} cd
73 @deftypefnx {} {@var{old_dir} =} cd
74 @deftypefnx {} {@var{old_dir} =} cd (@var{dir})
75 @deftypefnx {} {} chdir @dots{}
76 Change the current working directory to @var{dir}.
77 
78 If called with no input or output arguments, the current directory is
79 changed to the user's home directory (@qcode{"~"}).
80 
81 For example,
82 
83 @example
84 cd ~/octave
85 @end example
86 
87 @noindent
88 changes the current working directory to @file{~/octave}. If the
89 directory does not exist, an error message is printed and the working
90 directory is not changed.
91 
92 @code{chdir} is an alias for @code{cd} and can be used in all of the same
93 calling formats.
94 
95 Compatibility Note: When called with no arguments, @sc{matlab} prints the
96 present working directory rather than changing to the user's home directory.
97 @seealso{pwd, mkdir, rmdir, dir, ls}
98 @end deftypefn */)
99 {
100  int nargin = args.length ();
101 
102  if (nargin > 1)
103  print_usage ();
104 
106 
107  if (nargout > 0)
109 
110  if (nargin == 1)
111  {
112  std::string dirname = args(0).xstring_value ("cd: DIR must be a string");
113 
114  if (! dirname.empty ())
115  interp.chdir (dirname);
116  }
117  else if (nargout == 0)
118  {
119  std::string home_dir = octave::sys::env::get_home_directory ();
120 
121  if (! home_dir.empty ())
122  interp.chdir (home_dir);
123  }
124 
125  return retval;
126 }
127 
128 DEFALIAS (chdir, cd);
129 
130 DEFUN (pwd, , ,
131  doc: /* -*- texinfo -*-
132 @deftypefn {} {} pwd ()
133 @deftypefnx {} {@var{dir} =} pwd ()
134 Return the current working directory.
135 @seealso{cd, dir, ls, mkdir, rmdir}
136 @end deftypefn */)
137 {
139 }
140 
141 DEFUN (readdir, args, ,
142  doc: /* -*- texinfo -*-
143 @deftypefn {} {@var{files} =} readdir (@var{dir})
144 @deftypefnx {} {[@var{files}, @var{err}, @var{msg}] =} readdir (@var{dir})
145 Return the names of files in the directory @var{dir} as a cell array of
146 strings.
147 
148 If an error occurs, return an empty cell array in @var{files}.
149 If successful, @var{err} is 0 and @var{msg} is an empty string.
150 Otherwise, @var{err} is nonzero and @var{msg} contains a system-dependent
151 error message.
152 @seealso{ls, dir, glob, what}
153 @end deftypefn */)
154 {
155  if (args.length () != 1)
156  print_usage ();
157 
158  std::string dirname = args(0).xstring_value ("readdir: DIR must be a string");
159 
160  octave_value_list retval = ovl (Cell (), -1.0, "");
161 
163 
164  string_vector dirlist;
165  std::string msg;
166 
167  if (octave::sys::get_dirlist (dirname, dirlist, msg))
168  {
169  retval(0) = Cell (dirlist.sort ());
170  retval(1) = 0.0;
171  }
172  else
173  retval(2) = msg;
174 
175  return retval;
176 }
177 
178 // FIXME: should maybe also allow second arg to specify mode?
179 // OTOH, that might cause trouble with compatibility later...
180 
181 DEFUN (__mkdir__, args, ,
182  doc: /* -*- texinfo -*-
183 @deftypefn {} {} __mkdir__ (@var{parent}, @var{dir})
184 Internal function called by mkdir.m.
185 @seealso{mkdir, rmdir, pwd, cd, umask}
186 @end deftypefn */)
187 {
188  int nargin = args.length ();
189 
190  if (nargin < 1 || nargin > 2)
191  print_usage ("mkdir");
192 
193  std::string dirname;
194 
195  if (nargin == 2)
196  {
197  std::string parent = args(0).xstring_value ("mkdir: PARENT must be a string");
198  std::string dir = args(1).xstring_value ("mkdir: DIR must be a string");
199 
200  dirname = octave::sys::file_ops::concat (parent, dir);
201  }
202  else if (nargin == 1)
203  dirname = args(0).xstring_value ("mkdir: DIR must be a string");
204 
206 
208 
209  if (fs && fs.is_dir ())
210  {
211  // For Matlab compatibility, return true when directory already exists.
212  return ovl (true, "directory exists", "mkdir");
213  }
214  else
215  {
216  std::string msg;
217 
218  int status = octave::sys::mkdir (dirname, 0777, msg);
219 
220  if (status < 0)
221  return ovl (false, msg, "mkdir");
222  else
223  return ovl (true, "", "");
224  }
225 }
226 
227 DEFMETHODX ("rmdir", Frmdir, interp, args, ,
228  doc: /* -*- texinfo -*-
229 @deftypefn {} {} rmdir @var{dir}
230 @deftypefnx {} {} rmdir (@var{dir}, "s")
231 @deftypefnx {} {[@var{status}, @var{msg}, @var{msgid}] =} rmdir (@dots{})
232 Remove the directory named @var{dir}.
233 
234 If the optional second parameter is supplied with value @qcode{"s"},
235 recursively remove all subdirectories as well.
236 
237 If successful, @var{status} is 1, and @var{msg}, @var{msgid} are empty
238 character strings (""). Otherwise, @var{status} is 0, @var{msg} contains a
239 system-dependent error message, and @var{msgid} contains a unique message
240 identifier.
241 
242 @seealso{mkdir, confirm_recursive_rmdir, pwd}
243 @end deftypefn */)
244 {
245  int nargin = args.length ();
246 
247  if (nargin < 1 || nargin > 2)
248  print_usage ();
249 
250  std::string dirname = args(0).xstring_value ("rmdir: DIR must be a string");
251 
252  std::string fulldir = octave::sys::file_ops::tilde_expand (dirname);
253  int status = -1;
254  std::string msg;
255 
256  octave::event_manager& evmgr = interp.get_event_manager ();
257 
258  if (nargin == 2)
259  {
260  if (args(1).string_value () != "s")
261  error (R"(rmdir: second argument must be "s" for recursive removal)");
262 
263  bool doit = true;
264 
265  if (interp.interactive ()
268  {
269  octave::input_system& input_sys = interp.get_input_system ();
270 
271  std::string prompt = "remove entire contents of " + fulldir + "? ";
272 
273  doit = input_sys.yes_or_no (prompt);
274  }
275 
276  if (doit)
277  {
278  evmgr.file_remove (fulldir, "");
279  status = octave::sys::recursive_rmdir (fulldir, msg);
280  }
281  }
282  else
283  {
284  evmgr.file_remove (fulldir, "");
285  status = octave::sys::rmdir (fulldir, msg);
286  }
287 
288  evmgr.file_renamed (status >= 0);
289 
290  if (status < 0)
291  return ovl (false, msg, "rmdir");
292  else
293  return ovl (true, "", "");
294 }
295 
296 DEFUNX ("link", Flink, args, ,
297  doc: /* -*- texinfo -*-
298 @deftypefn {} {} link @var{old} @var{new}
299 @deftypefnx {} {[@var{err}, @var{msg}] =} link (@var{old}, @var{new})
300 Create a new link (also known as a hard link) to an existing file.
301 
302 If successful, @var{err} is 0 and @var{msg} is an empty string.
303 Otherwise, @var{err} is nonzero and @var{msg} contains a system-dependent
304 error message.
305 @seealso{symlink, unlink, readlink, lstat}
306 @end deftypefn */)
307 {
308  if (args.length () != 2)
309  print_usage ();
310 
311  std::string from = args(0).xstring_value ("link: OLD must be a string");
312  std::string to = args(1).xstring_value ("link: NEW must be a string");
313 
316 
317  std::string msg;
318 
319  int status = octave::sys::link (from, to, msg);
320 
321  if (status < 0)
322  return ovl (-1.0, msg);
323  else
324  return ovl (status, "");
325 }
326 
327 DEFUNX ("symlink", Fsymlink, args, ,
328  doc: /* -*- texinfo -*-
329 @deftypefn {} {} symlink @var{old} @var{new}
330 @deftypefnx {} {[@var{err}, @var{msg}] =} symlink (@var{old}, @var{new})
331 Create a symbolic link @var{new} which contains the string @var{old}.
332 
333 If successful, @var{err} is 0 and @var{msg} is an empty string.
334 Otherwise, @var{err} is nonzero and @var{msg} contains a system-dependent
335 error message.
336 @seealso{link, unlink, readlink, lstat}
337 @end deftypefn */)
338 {
339  if (args.length () != 2)
340  print_usage ();
341 
342  std::string from = args(0).xstring_value ("symlink: OLD must be a string");
343  std::string to = args(1).xstring_value ("symlink: NEW must be a string");
344 
347 
348  std::string msg;
349 
350  int status = octave::sys::symlink (from, to, msg);
351 
352  if (status < 0)
353  return ovl (-1.0, msg);
354  else
355  return ovl (status, "");
356 }
357 
358 DEFUNX ("readlink", Freadlink, args, ,
359  doc: /* -*- texinfo -*-
360 @deftypefn {} {} readlink @var{symlink}
361 @deftypefnx {} {[@var{result}, @var{err}, @var{msg}] =} readlink (@var{symlink})
362 Read the value of the symbolic link @var{symlink}.
363 
364 If successful, @var{result} contains the contents of the symbolic link
365 @var{symlink}, @var{err} is 0, and @var{msg} is an empty string.
366 Otherwise, @var{err} is nonzero and @var{msg} contains a system-dependent
367 error message.
368 @seealso{lstat, symlink, link, unlink, delete}
369 @end deftypefn */)
370 {
371  if (args.length () != 1)
372  print_usage ();
373 
374  std::string symlink = args(0).xstring_value ("readlink: SYMLINK must be a string");
375 
377 
378  std::string result, msg;
379 
380  int status = octave::sys::readlink (symlink, result, msg);
381 
382  if (status < 0)
383  return ovl ("", -1.0, msg);
384  else
385  return ovl (result, status, "");
386 }
387 
388 DEFMETHODX ("rename", Frename, interp, args, ,
389  doc: /* -*- texinfo -*-
390 @deftypefn {} {} rename @var{old} @var{new}
391 @deftypefnx {} {[@var{err}, @var{msg}] =} rename (@var{old}, @var{new})
392 Change the name of file @var{old} to @var{new}.
393 
394 If successful, @var{err} is 0 and @var{msg} is an empty string.
395 Otherwise, @var{err} is nonzero and @var{msg} contains a system-dependent
396 error message.
397 @seealso{movefile, copyfile, ls, dir}
398 @end deftypefn */)
399 {
400  if (args.length () != 2)
401  print_usage ();
402 
403  std::string from = args(0).xstring_value ("rename: OLD must be a string");
404  std::string to = args(1).xstring_value ("rename: NEW must be a string");
405 
408 
409  std::string msg;
410 
411  octave::event_manager& evmgr = interp.get_event_manager ();
412 
413  evmgr.file_remove (from, to);
414 
415  int status = octave::sys::rename (from, to, msg);
416 
417  if (status < 0)
418  {
419  evmgr.file_renamed (false);
420  return ovl (-1.0, msg);
421  }
422  else
423  {
424  evmgr.file_renamed (true);
425  return ovl (status, "");
426  }
427 }
428 
429 DEFUN (glob, args, ,
430  doc: /* -*- texinfo -*-
431 @deftypefn {} {} glob (@var{pattern})
432 Given an array of pattern strings (as a char array or a cell array) in
433 @var{pattern}, return a cell array of filenames that match any of
434 them, or an empty cell array if no patterns match.
435 
436 The pattern strings are interpreted as filename globbing patterns (as they
437 are used by Unix shells).
438 
439 Within a pattern
440 
441 @table @code
442 @item *
443 matches any string, including the null string,
444 
445 @item ?
446 matches any single character, and
447 
448 @item [@dots{}]
449 matches any of the enclosed characters.
450 @end table
451 
452 Tilde expansion is performed on each of the patterns before looking for
453 matching filenames. For example:
454 
455 @example
456 ls
457  @result{}
458  file1 file2 file3 myfile1 myfile1b
459 glob ("*file1")
460  @result{}
461  @{
462  [1,1] = file1
463  [2,1] = myfile1
464  @}
465 glob ("myfile?")
466  @result{}
467  @{
468  [1,1] = myfile1
469  @}
470 glob ("file[12]")
471  @result{}
472  @{
473  [1,1] = file1
474  [2,1] = file2
475  @}
476 @end example
477 @seealso{ls, dir, readdir, what}
478 @end deftypefn */)
479 {
480  if (args.length () != 1)
481  print_usage ();
482 
483  string_vector pat = args(0).xstring_vector_value ("glob: PATTERN must be a string");
484 
486 
487  return ovl (Cell (pattern.glob ()));
488 }
489 
490 
491 DEFUN (__wglob__, args, ,
492  doc: /* -*- texinfo -*-
493 @deftypefn {} {} __wglob__ (@var{pattern})
494 Windows-like glob for dir.
495 
496 Given an array of pattern strings (as a char array or a cell array) in
497 @var{pattern}, return a cell array of filenames that match any of
498 them, or an empty cell array if no patterns match.
499 
500 The pattern strings are interpreted as filename globbing patterns
501 (roughly as they are used by Windows dir).
502 
503 Within a pattern
504 
505 @table @code
506 @item *
507 matches any string, including the null string,
508 
509 @item ?
510 matches any single character, and
511 
512 @item *.*
513 matches any string, even if no . is present.
514 @end table
515 
516 Tilde expansion is performed on each of the patterns before looking for
517 matching filenames. For example:
518 
519 @example
520 ls
521  @result{}
522  file1 file2 file3 myfile1 myfile1b
523 glob ("*file1")
524  @result{}
525  @{
526  [1,1] = file1
527  [2,1] = myfile1
528  @}
529 glob ("myfile?")
530  @result{}
531  @{
532  [1,1] = myfile1
533  @}
534 glob ("*.*")
535  @result{}
536  @{
537  [1,1] = file1
538  [2,1] = file2
539  [3,1] = file3
540  [4,1] = myfile1
541  [5,1] = myfile1b
542  @}
543 @end example
544 @seealso{glob, dir}
545 @end deftypefn */)
546 {
547  if (args.length () == 0)
548  return ovl ();
549 
550  string_vector pat = args(0).string_vector_value ();
551 
553 
554  return ovl (Cell (octave::sys::windows_glob (pattern)));
555 }
556 
557 /*
558 %!test
559 %! tmpdir = tempname;
560 %! filename = {"file1", "file2", "file3", "myfile1", "myfile1b"};
561 %! if (mkdir (tmpdir))
562 %! cwd = pwd;
563 %! cd (tmpdir);
564 %! if (strcmp (canonicalize_file_name (pwd), canonicalize_file_name (tmpdir)))
565 %! a = 0;
566 %! for n = 1:5
567 %! save (filename{n}, "a");
568 %! endfor
569 %! else
570 %! rmdir (tmpdir);
571 %! error ("Couldn't change to temporary dir");
572 %! endif
573 %! else
574 %! error ("Couldn't create temporary directory");
575 %! endif
576 %! result1 = glob ("*file1");
577 %! result2 = glob ("myfile?");
578 %! result3 = glob ("file[12]");
579 %! for n = 1:5
580 %! delete (filename{n});
581 %! endfor
582 %! cd (cwd);
583 %! rmdir (tmpdir);
584 %! assert (result1, {"file1"; "myfile1"});
585 %! assert (result2, {"myfile1"});
586 %! assert (result3, {"file1"; "file2"});
587 */
588 
589 DEFUN (__fnmatch__, args, ,
590  doc: /* -*- texinfo -*-
591 @deftypefn {} {} fnmatch (@var{pattern}, @var{string})
592 Return true or false for each element of @var{string} that matches any of
593 the elements of the string array @var{pattern}, using the rules of
594 filename pattern matching.
595 
596 For example:
597 
598 @example
599 @group
600 fnmatch ("a*b", @{"ab"; "axyzb"; "xyzab"@})
601  @result{} [ 1; 1; 0 ]
602 @end group
603 @end example
604 @seealso{glob, regexp}
605 @end deftypefn */)
606 {
607  if (args.length () != 2)
608  print_usage ();
609 
610  string_vector pat = args(0).string_vector_value ();
611  string_vector str = args(1).string_vector_value ();
612 
614 
615  return ovl (pattern.match (str));
616 }
617 
618 DEFUN (filesep, args, ,
619  doc: /* -*- texinfo -*-
620 @deftypefn {} {} filesep ()
621 @deftypefnx {} {} filesep ("all")
622 Return the system-dependent character used to separate directory names.
623 
624 If @qcode{"all"} is given, the function returns all valid file separators
625 in the form of a string. The list of file separators is system-dependent.
626 It is @samp{/} (forward slash) under UNIX or @w{Mac OS X}, @samp{/} and
627 @samp{\} (forward and backward slashes) under Windows.
628 @seealso{pathsep}
629 @end deftypefn */)
630 {
631  int nargin = args.length ();
632 
633  if (nargin > 1)
634  print_usage ();
635 
637 
638  if (nargin == 0)
640  else
641  {
642  std::string s = args(0).xstring_value ("filesep: argument must be a string");
643  if (s != "all")
644  error (R"(filesep: argument must be "all")");
645 
647  }
648 
649  return retval;
650 }
651 
652 DEFUN (pathsep, args, ,
653  doc: /* -*- texinfo -*-
654 @deftypefn {} {@var{val} =} pathsep ()
655 Query the character used to separate directories in a path.
656 @seealso{filesep}
657 @end deftypefn */)
658 {
659  int nargin = args.length ();
660 
661  if (nargin > 0)
662  print_usage ();
663 
665 }
666 
667 DEFUN (confirm_recursive_rmdir, args, nargout,
668  doc: /* -*- texinfo -*-
669 @deftypefn {} {@var{val} =} confirm_recursive_rmdir ()
670 @deftypefnx {} {@var{old_val} =} confirm_recursive_rmdir (@var{new_val})
671 @deftypefnx {} {} confirm_recursive_rmdir (@var{new_val}, "local")
672 Query or set the internal variable that controls whether Octave
673 will ask for confirmation before recursively removing a directory tree.
674 
675 When called from inside a function with the @qcode{"local"} option, the
676 variable is changed locally for the function and any subroutines it calls.
677 The original variable value is restored when exiting the function.
678 @seealso{rmdir}
679 @end deftypefn */)
680 {
681  return SET_INTERNAL_VARIABLE (confirm_recursive_rmdir);
682 }
Definition: Cell.h:43
string_vector glob(void) const
Definition: glob-match.cc:41
bool match(const std::string &str) const
Definition: glob-match.cc:35
static bool forced_interactive(void)
Definition: octave.cc:280
static std::string path_sep_str(void)
Definition: pathsearch.cc:127
Provides threadsafe access to octave.
void file_renamed(bool load_new)
void file_remove(const std::string &old_name, const std::string &new_name)
bool yes_or_no(const std::string &prompt)
bool is_dir(void) const
Definition: file-stat.cc:65
static std::string get_home_directory(void)
Definition: oct-env.cc:147
static std::string get_current_directory(void)
Definition: oct-env.cc:140
string_vector & sort(bool make_uniq=false)
Definition: str-vec.cc:77
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#define DEFMETHOD(name, interp_name, args_name, nargout_name, doc)
Macro to define a builtin method.
Definition: defun.h:138
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
#define DEFUNX(name, fname, args_name, nargout_name, doc)
Macro to define a builtin function with certain internal name.
Definition: defun.h:85
#define DEFALIAS(alias, name)
Macro to define an alias for another existing function name.
Definition: defun.h:214
static bool Vconfirm_recursive_rmdir
Definition: dirfns.cc:67
OCTAVE_EXPORT octave_value_list Flink(const octave_value_list &args, int)
Definition: dirfns.cc:306
OCTAVE_EXPORT octave_value_list Frmdir(octave::interpreter &interp, const octave_value_list &args, int)
Definition: dirfns.cc:243
OCTAVE_EXPORT octave_value_list Fsymlink(const octave_value_list &args, int)
Definition: dirfns.cc:337
OCTAVE_EXPORT octave_value_list Frename(octave::interpreter &interp, const octave_value_list &args, int)
Definition: dirfns.cc:398
OCTAVE_EXPORT octave_value_list Freadlink(const octave_value_list &args, int)
Definition: dirfns.cc:369
void error(const char *fmt,...)
Definition: error.cc:968
std::string dirname(const std::string &path)
Definition: file-ops.cc:363
std::string dir_sep_str(void)
Definition: file-ops.cc:243
std::string tilde_expand(const std::string &name)
Definition: file-ops.cc:286
std::string concat(const std::string &dir, const std::string &file)
Definition: file-ops.cc:354
std::string dir_sep_chars(void)
Definition: file-ops.cc:252
int link(const std::string &old_name, const std::string &new_name)
Definition: file-ops.cc:440
int readlink(const std::string &path, std::string &result)
Definition: file-ops.cc:482
int rename(const std::string &from, const std::string &to)
Definition: file-ops.cc:508
string_vector windows_glob(const string_vector &pat)
Definition: oct-glob.cc:149
int mkdir(const std::string &nm, mode_t md)
Definition: file-ops.cc:404
int symlink(const std::string &old_name, const std::string &new_name)
Definition: file-ops.cc:461
int rmdir(const std::string &name)
Definition: file-ops.cc:535
int recursive_rmdir(const std::string &name)
Definition: file-ops.cc:557
int chdir(const std::string &path_arg)
Definition: lo-sysdep.cc:88
string_vector glob(const string_vector &pat)
Definition: oct-glob.cc:73
bool get_dirlist(const std::string &dirname, string_vector &dirlist, std::string &msg)
Definition: lo-sysdep.cc:101
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
DEFMETHODX("quad", Fquad, interp, args,, doc:)
Definition: quad.cc:130
#define SET_INTERNAL_VARIABLE(NM)
Definition: variables.h:103