1 ## Copyright (C) 2001-2012 Paul Kienzle
3 ## This file is part of Octave.
5 ## Octave is free software; you can redistribute it and/or modify it
6 ## under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 3 of the License, or (at
8 ## your option) any later version.
10 ## Octave is distributed in the hope that it will be useful, but
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ## General Public License for more details.
15 ## You should have received a copy of the GNU General Public License
16 ## along with Octave; see the file COPYING. If not, see
17 ## <http://www.gnu.org/licenses/>.
20 ## @deftypefn {Command} {} edit @var{name}
21 ## @deftypefnx {Command} {} edit @var{field} @var{value}
22 ## @deftypefnx {Command} {@var{value} =} edit get @var{field}
23 ## Edit the named function, or change editor settings.
25 ## If @code{edit} is called with the name of a file or function as
26 ## its argument it will be opened in a text editor.
30 ## If the function @var{name} is available in a file on your path and
31 ## that file is modifiable, then it will be edited in place. If it
32 ## is a system function, then it will first be copied to the directory
33 ## @env{HOME} (see further down) and then edited.
34 ## If no file is found, then the m-file
35 ## variant, ending with ".m", will be considered. If still no file
36 ## is found, then variants with a leading "@@" and then with both a
37 ## leading "@@" and trailing ".m" will be considered.
40 ## If @var{name} is the name of a function defined in the interpreter but
41 ## not in an m-file, then an m-file will be created in @env{HOME}
42 ## to contain that function along with its current definition.
45 ## If @code{name.cc} is specified, then it will search for @code{name.cc}
46 ## in the path and try to modify it, otherwise it will create a new
47 ## @file{.cc} file in @env{HOME}. If @var{name} happens to be an
48 ## m-file or interpreter defined function, then the text of that
49 ## function will be inserted into the .cc file as a comment.
52 ## If @var{name.ext} is on your path then it will be edited, otherwise
53 ## the editor will be started with @file{HOME/name.ext} as the
54 ## filename. If @file{name.ext} is not modifiable, it will be copied to
55 ## @env{HOME} before editing.
57 ## @strong{Warning:} You may need to clear name before the new definition
58 ## is available. If you are editing a .cc file, you will need
59 ## to mkoctfile @file{name.cc} before the definition will be available.
62 ## If @code{edit} is called with @var{field} and @var{value} variables,
63 ## the value of the control field @var{field} will be @var{value}.
64 ## If an output argument is requested and the first argument is @code{get}
65 ## then @code{edit} will return the value of the control field @var{field}.
66 ## If the control field does not exist, edit will return a structure
67 ## containing all fields and values. Thus, @code{edit get all} returns
68 ## a complete control structure.
69 ## The following control fields are used:
73 ## This is the editor to use to modify the functions. By default it uses
74 ## Octave's @env{EDITOR} built-in function, which comes from
75 ## @code{getenv("EDITOR")} and defaults to @code{emacs}. Use @code{%s}
76 ## In place of the function name. For example,
78 ## @item [EDITOR, " %s"]
79 ## Use the editor which Octave uses for @code{edit_history}.
82 ## pop up simple X11 editor in a separate window
84 ## @item "gnudoit -q \"(find-file \\\"%s\\\")\""
85 ## Send it to current Emacs; must have @code{(gnuserv-start)} in @file{.emacs}.
88 ## See also field 'mode', which controls how the editor is run by Octave.
90 ## On Cygwin, you will need to convert the Cygwin path to a Windows
91 ## path if you are using a native Windows editor. For example:
92 ## @c Set example in small font to prevent overfull line in TeX
95 ## @exdent '"C:/Program Files/Good Editor/Editor.exe" "$(cygpath -wa %s)"'
99 ## This is the location of user local m-files. Be be sure it is in your
100 ## path. The default is @file{~/octave}.
103 ## This is the name to put after the "## Author:" field of new functions.
104 ## By default it guesses from the @code{gecos} field of password database.
107 ## This is the e-mail address to list after the name in the author field.
108 ## By default it guesses @code{<$LOGNAME@@$HOSTNAME>}, and if @code{$HOSTNAME}
109 ## is not defined it uses @code{uname -n}. You probably want to override this.
110 ## Be sure to use @code{<user@@host>} as your format.
115 ## GNU General Public License (default).
118 ## BSD-style license without advertising clause.
124 ## Your own default copyright and license.
127 ## Unless you specify @samp{pd}, edit will prepend the copyright statement
128 ## with "Copyright (C) yyyy Function Author".
131 ## This value determines whether the editor should be started in async mode
132 ## (editor is started in the background and Octave continues) or sync mode
133 ## (Octave waits until the editor exits). Set it to "sync" to start the editor
134 ## in sync mode. The default is "async" (see also "system").
137 ## Determines whether files should be edited in place, without regard to
138 ## whether they are modifiable or not. The default is @code{false}.
142 ## Author: Paul Kienzle <pkienzle@users.sf.net>
144 ## Original version by Paul Kienzle distributed as free software in the
147 function ret = edit (file, state)
149 ## Pick up globals or default them.
151 persistent FUNCTION = struct ("EDITOR", cstrcat (EDITOR (), " %s"),
152 "HOME", fullfile (default_home, "octave"),
153 "AUTHOR", default_user(1),
157 "EDITINPLACE", false);
158 ## Make sure the state variables survive "clear functions".
162 switch (toupper (file))
164 FUNCTION.EDITOR = state;
166 if (! isempty (state) && state(1) == "~")
167 state = [ default_home, state(2:end) ];
169 FUNCTION.HOME = state;
171 FUNCTION.AUTHOR = state;
173 FUNCTION.EMAIL = state;
175 FUNCTION.LICENSE = state;
177 if (strcmp (state, "sync") || strcmp (state, "async"))
178 FUNCTION.MODE = state;
180 error('edit: expected "edit MODE sync|async"');
184 if (strcmpi (state, "true"))
186 elseif (strcmpi (state, "false"))
189 state = eval (state);
192 FUNCTION.EDITINPLACE = state;
194 if (isfield (FUNCTION, toupper(state)))
195 ret = FUNCTION.(toupper (state));
200 error ('edit: expected "edit EDITOR|HOME|AUTHOR|EMAIL|LICENSE|MODE val"');
205 ## Start the editor without a file if no file is given.
207 if (exist (FUNCTION.HOME, "dir") == 7 && (isunix () || ! ispc ()))
208 system (cstrcat ("cd \"", FUNCTION.HOME, "\" ; ",
209 sprintf (FUNCTION.EDITOR, "")),
212 system (sprintf (FUNCTION.EDITOR,""), [], FUNCTION.MODE);
217 ## Check whether the user is trying to edit a builtin of compiled function.
218 switch (exist (file))
220 error ("edit: unable to edit a built-in or compiled function");
223 ## Checks for whether the file is
224 ## absolute or relative should be handled inside file_in_loadpath.
225 ## That way, it will be possible to look up files correctly given
226 ## partial path information. For example, you should be able to
227 ## edit a particular overloaded function by doing any one of
229 ## edit classname/foo
230 ## edit classname/foo.m
231 ## edit @classname/foo
232 ## edit @classname/foo.m
234 ## This functionality is needed for other functions as well (at least
235 ## help and type; there may be more). So the place to fix that is in
236 ## file_in_loadpath, possibly with some help from the load_path class.
238 ## The code below includes a portion that serves as a place-holder for
239 ## the changes suggested above.
241 ## Create list of explicit and implicit file names.
243 ## If file has no extension, add file.m and file.cc to the list.
244 idx = rindex (file, ".");
246 ## Create the list of files to look for
248 if (isempty (regexp (file, '\.m$')))
249 ## No ".m" at the end of the file, add to the list.
250 filelist{end+1} = cat (2, file, ".m");
252 if (isempty (regexp (file, '\.cc$')))
253 ## No ".cc" at the end of the file, add to the list.
254 filelist{end+1} = cat (2, file, ".cc");
258 ## If the file includes a path, it may be an overloaded function.
259 if (! strcmp (file, "@") && index (file, filesep))
260 ## No "@" at the beginning of the file, add to the list.
261 numfiles = numel(filelist);
263 filelist{n+numfiles} = cat (2, "@", filelist{n});
267 ## Search the entire path for the 1st instance of a file in the list.
269 for n = 1:numel(filelist)
270 filetoedit = file_in_path (path, filelist{n});
271 if (! isempty (filetoedit))
272 ## The path is explicitly included.
273 fileandpath = filetoedit;
278 if (! isempty (fileandpath))
279 ## If the file exists, then edit it.
280 if (FUNCTION.EDITINPLACE)
281 ## Edit in place even if it is protected.
282 system (sprintf (FUNCTION.EDITOR, cstrcat ("\"", fileandpath, "\"")),
286 ## If the file is modifiable in place then edit it, otherwise make
287 ## a copy in HOME and then edit it.
288 fid = fopen (fileandpath, "r+t");
291 fileandpath = cstrcat (FUNCTION.HOME, from (rindex (from, filesep):end));
292 [status, msg] = copyfile (from, fileandpath, 1);
299 system (sprintf (FUNCTION.EDITOR, cstrcat ("\"", fileandpath, "\"")),
305 ## If editing a new file that is neither a m-file or an oct-file,
308 idx = rindex (file, ".");
309 name = file(1:idx-1);
310 ext = file(idx+1:end);
315 system (sprintf (FUNCTION.EDITOR, cstrcat ("\"", fileandpath, "\"")),
320 ## The file doesn't exist in path so create it, put in the function
321 ## template and edit it.
323 ## Guess the email name if it was not given.
324 if (isempty (FUNCTION.EMAIL))
325 host = getenv("HOSTNAME");
326 if (isempty (host) && ispc ())
327 host = getenv ("COMPUTERNAME");
330 [status, host] = system ("uname -n");
331 ## trim newline from end of hostname
332 if (! isempty (host))
333 host = host(1:end-1);
337 FUNCTION.EMAIL = " ";
339 FUNCTION.EMAIL = cstrcat ("<", default_user(0), "@", host, ">");
343 ## Fill in the revision string.
344 now = localtime (time);
345 revs = cstrcat ("Created: ", strftime ("%Y-%m-%d", now));
347 ## Fill in the copyright string.
348 copyright = cstrcat (strftime ("Copyright (C) %Y ", now), FUNCTION.AUTHOR);
350 ## Fill in the author tag field.
351 author = cstrcat ("Author: ", FUNCTION.AUTHOR, " ", FUNCTION.EMAIL);
353 ## Fill in the header.
354 uclicense = toupper (FUNCTION.LICENSE);
357 head = cstrcat (copyright, "\n\n", "\
358 This program is free software; you can redistribute it and/or modify\n\
359 it under the terms of the GNU General Public License as published by\n\
360 the Free Software Foundation; either version 3 of the License, or\n\
361 (at your option) any later version.\n\
363 This program is distributed in the hope that it will be useful,\n\
364 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
365 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
366 GNU General Public License for more details.\n\
368 You should have received a copy of the GNU General Public License\n\
369 along with Octave; see the file COPYING. If not, see\n\
370 <http://www.gnu.org/licenses/>.\
372 tail = cstrcat (author, "\n", revs);
375 head = cstrcat (copyright, "\n\n", "\
376 This program is free software; redistribution and use in source and\n\
377 binary forms, with or without modification, are permitted provided that\n\
378 the following conditions are met:\n\
380 1.Redistributions of source code must retain the above copyright\n\
381 notice, this list of conditions and the following disclaimer.\n\
382 2.Redistributions in binary form must reproduce the above copyright\n\
383 notice, this list of conditions and the following disclaimer in the\n\
384 documentation and/or other materials provided with the distribution.\n\
386 THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n\
387 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n\
388 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\
389 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n\
390 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n\
391 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n\
392 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n\
393 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n\
394 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n\
395 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n\
398 tail = cstrcat (author, "\n", revs);
402 tail = cstrcat (author, "\n", revs, "\n\n",
403 "This program is granted to the public domain.");
407 tail = cstrcat (copyright, "\n\n", FUNCTION.LICENSE, "\n",
411 ## Generate the function template.
412 exists = exist (name);
414 case {"cc", "C", "cpp"}
416 comment = cstrcat ("/*\n", tail, "\n\n*/\n\n");
418 comment = cstrcat ("/*\n", head, "\n\n", tail, "\n\n*/\n\n");
420 ## If we are shadowing an m-file, paste the code for the m-file.
421 if (any (exists == [2, 103]))
422 code = cstrcat ("\\ ", strrep (type (name), "\n", "\n// "));
426 body = cstrcat ("#include <octave/oct.h>\n\n",
427 "DEFUN_DLD(", name, ",args,nargout,\"\\\n",
428 name, "\\n\\\n\")\n{\n",
429 " octave_value_list retval;\n",
430 " int nargin = args.length();\n\n",
431 code, "\n return retval;\n}\n");
433 text = cstrcat (comment, body);
435 ## If we are editing a function defined on the fly, paste the
437 if (any (exists == [2, 103]))
440 body = cstrcat ("function [ ret ] = ", name, " ()\n\nendfunction\n");
443 comment = cstrcat ("## ", name, "\n\n",
444 "## ", strrep (tail, "\n", "\n## "), "\n\n");
446 comment = cstrcat ("## ", strrep(head,"\n","\n## "), "\n\n", ...
447 "## ", name, "\n\n", ...
448 "## ", strrep (tail, "\n", "\n## "), "\n\n");
450 text = cstrcat (comment, body);
453 ## Write the initial file (if there is anything to write)
454 fid = fopen (fileandpath, "wt");
456 error ("edit: could not create %s", fileandpath);
461 ## Finally we are ready to edit it!
462 system (sprintf (FUNCTION.EDITOR, cstrcat ("\"", fileandpath, "\"")),
467 function ret = default_home ()
469 ret = getenv ("HOME");
481 ## Return the name associated with the current user ID.
483 ## If LONG_FORM is 1, return the full name. This will be the
484 ## default author. Otherwise return the login name.
485 ## login@host will be the default email address.
487 function ret = default_user (long_form)
489 ent = getpwuid (getuid);
490 if (! isstruct (ent))
491 ret = getenv ("USER");
493 ret = getenv ("USERNAME");
497 pos = strfind (ret, ",");
508 %! s.editor = edit ("get", "editor");
509 %! s.home = edit ("get", "home");
510 %! s.author = edit ("get", "author");
511 %! s.email = edit ("get", "email");
512 %! s.license = edit ("get", "license");
513 %! s.editinplace = edit ("get", "editinplace");
514 %! s.mode = edit ("get", "mode");
520 %! edit ("editinplace", !s.editinplace)
521 %! if (s.mode(1) == "a")
526 %! edit ("editor", s.editor);
527 %! edit ("home", s.home);
528 %! edit ("author", s.author);
529 %! edit ("email", s.email);
530 %! edit ("license", s.license);
531 %! edit ("editinplace", s.editinplace);
532 %! edit ("mode", s.mode);
533 %! assert (edit ("get", "editor"), s.editor);
534 %! assert (edit ("get", "home"), s.home);
535 %! assert (edit ("get", "author"), s.author);
536 %! assert (edit ("get", "email"), s.email);
537 %! assert (edit ("get", "license"), s.license);
538 %! assert (edit ("get", "editinplace"), s.editinplace);
539 %! assert (edit ("get", "mode"), s.mode);